Giter Site home page Giter Site logo

davidwells / markdown-magic Goto Github PK

View Code? Open in Web Editor NEW
807.0 14.0 227.0 883 KB

💫  Automatically format markdown files via comment blocks using source code, external data & custom transforms.

JavaScript 100.00%
markdown readme docs docs-generator dx

markdown-magic's Introduction

Markdown Magic npm-version

✨ Add a little magic to your markdown ✨

About

Markdown magic uses comment blocks in markdown files to automatically sync or transform its contents.

  • Automatically keep markdown files up to date from local or remote code sources
  • Transform markdown content with custom transform functions
  • Render markdown with any template engine
  • Automatically generate a table of contents
  • ... etc

The comments markdown magic uses are hidden in markdown and when viewed as HTML.

This README.md is generated with markdown-magic view the raw file to see how.

Video demoExample Repo

Table of Contents

Click to expand

Install

To get started. Install the npm package.

npm install markdown-magic --save-dev

Usage

Use comment blocks in your markdown

Example:

<!-- doc-gen remote url=http://url-to-raw-md-file.md -->
This content will be dynamically replaced from the remote url
<!-- end-doc-gen -->

Then run markdown-magic via it's CLI or programmatically.

Running via CLI

Run markdown --help to see all available CLI options

markdown
# or
md-magic

CLI usage example with options

md-magic --path '**/*.md' --config ./config.file.js

In NPM scripts, npm run docs would run the markdown magic and parse all the .md files in the directory.

"scripts": {
  "docs": "md-magic --path '**/*.md'"
},

If you have a markdown.config.js file where markdown-magic is invoked, it will automatically use that as the configuration unless otherwise specified by --config flag.

Running programmatically

const { markdownMagic } = require('../lib')

/* By default all .md files in cwd will be processed */
markdownMagic().then((results) => {
  console.log('result keys', Object.keys(results))
})
import path from 'path'
import markdownMagic from 'markdown-magic'

// Process a Single File
const markdownPath = path.join(__dirname, 'README.md')
markdownMagic(markdownPath)

Syntax Examples

There are various syntax options. Choose your favorite.

Basic

openWord transformName [opts]

<!-- doc-gen transformName optionOne='hello' optionTwo='there' -->
content to be replaced
<!-- end-doc-gen -->

Curly braces

openWord {transformName} [opts]

<!-- doc-gen {transformName} optionOne='hello' optionTwo='there' -->
content to be replaced
<!-- end-doc-gen -->

Square brackets

openWord [transformName] [opts]

<!-- doc-gen [transformName] optionOne='hello' optionTwo='there' -->
content to be replaced
<!-- end-doc-gen -->

Parentheses

openWord (transformName) [opts]

<!-- doc-gen (transformName) optionOne='hello' optionTwo='there' -->
content to be replaced
<!-- end-doc-gen -->

Functions

openWord transformName([opts])

<!-- doc-gen transformName(
  foo='bar'
  baz=['qux', 'quux']
) -->
content to be replaced
<!-- end-doc-gen -->

API

Markdown Magic Instance

markdownMagic(globOrOpts, options)
Name Type Description
globOrOpts FilePathsOrGlobs or MarkdownMagicOptions Files to process or config.
options (optional) MarkdownMagicOptions Markdown magic config.

Returns

Promise<MarkdownMagicResult>

Example

markdownMagic(['**.**.md'], options).then((result) => {
  console.log(`Processing complete`, result)
})

MarkdownMagicOptions

Configuration for markdown magic

Below is the main config for markdown-magic

Name Type Description
files (optional) FilePathsOrGlobs Files to process.
transforms (optional) Array Custom commands to transform block contents, see transforms & custom transforms sections below. Default: defaultTransforms
output (optional) OutputConfig Output configuration.
syntax (optional) SyntaxType Syntax to parse. Default: md
open (optional) string Opening match word. Default: doc-gen
close (optional) string Closing match word. If not defined will be same as opening word. Default: end-doc-gen
cwd (optional) string Current working directory. Default process.cwd(). Default: process.cwd()
outputFlatten (optional) boolean Flatten files that are output.
useGitGlob (optional) boolean Use git glob for LARGE file directories.
dryRun (optional) boolean See planned execution of matched blocks. Default: false
debug (optional) boolean See debug details. Default: false
silent (optional) boolean Silence all console output. Default: false
failOnMissingTransforms (optional) boolean Fail if transform functions are missing. Default skip blocks. Default: false

OutputConfig

Optional output configuration

Name Type Description
directory (optional) string Change output path of new content. Default behavior is replacing the original file.
removeComments (optional) boolean Remove comments from output. Default is false. Default: false
pathFormatter (optional) function Custom function for altering output paths.
applyTransformsToSource (optional) boolean Apply transforms to source file. Default is true. This is for when outputDir is set. Default: false

MarkdownMagicResult

Result of markdown processing

Name Type Description
errors Array Any errors encountered.
filesChanged Array<string> Modified files.
results Array md data.

Transforms

Markdown Magic comes with a couple of built-in transforms for you to use or you can extend it with your own transforms. See 'Custom Transforms' below.

> TOC

Generate table of contents from markdown file

Options:

  • firsth1 - boolean - (optional): Show first h1 of doc in table of contents. Default false
  • collapse - boolean - (optional): Collapse the table of contents in a detail accordian. Default false
  • collapseText - string - (optional): Text the toc accordian summary
  • excludeText - string - (optional): Text to exclude in the table of contents. Default Table of Contents
  • maxDepth - number - (optional): Max depth of headings. Default 4

Example:

<!-- doc-gen TOC -->
toc will be generated here
<!-- end-doc-gen -->

Default MATCHWORD is AUTO-GENERATED-CONTENT


Name Type Description
content string The current content of the comment block.
options object The options passed in from the comment declaration.

> CODE

Get code from file or URL and put in markdown

Options:

  • src: The relative path to the code to pull in, or the URL where the raw code lives
  • syntax (optional): Syntax will be inferred by fileType if not specified
  • header (optional): Will add header comment to code snippet. Useful for pointing to relative source directory or adding live doc links
  • lines (optional): a range with lines of code which will then be replaced with code from the file. The line range should be defined as: "lines=startLine-EndLine" (for example: "lines=22-44"). Please see the example below

Example:

<!-- doc-gen CODE src="./relative/path/to/code.js" -->
This content will be dynamically replaced with code from the file
<!-- end-doc-gen -->
 <!-- doc-gen CODE src="./relative/path/to/code.js" lines=22-44 -->
 This content will be dynamically replaced with code from the file lines 22 through 44
 <!-- end-doc-gen -->

Default MATCHWORD is AUTO-GENERATED-CONTENT


Name Type Description
content string The current content of the comment block.
options object The options passed in from the comment declaration.

> FILE

Get local file contents.

Options:

  • src: The relative path to the file to pull in

Example:

<!-- doc-gen FILE src=./path/to/file -->
This content will be dynamically replaced from the local file
<!-- end-doc-gen -->

Default MATCHWORD is AUTO-GENERATED-CONTENT


Name Type Description
content string The current content of the comment block.
options object The options passed in from the comment declaration.

> REMOTE

Get any remote Data and put in markdown

Options:

  • url: The URL of the remote content to pull in

Example:

<!-- doc-gen REMOTE url=http://url-to-raw-md-file.md -->
This content will be dynamically replaced from the remote url
<!-- end-doc-gen -->

Default MATCHWORD is AUTO-GENERATED-CONTENT


Name Type Description
content string The current content of the comment block.
options object The options passed in from the comment declaration.

Inline transforms

Any transform, including custom transforms can be used inline as well to insert content into paragraphs and other places.

The face symbol 👉 ⊂◉‿◉つ is auto generated inline.

Example:

<!-- doc-gen (FILE:src=./path/to/file) -->xyz<!-- end-doc-gen -->

Legacy v1 & v2 plugins

These plugins work with older versions of markdown-magic. Adapting them to the newer plugin syntax should be pretty straight forward.

Adding Custom Transforms

Markdown Magic is extendable via plugins.

Plugins allow developers to add new transforms to the config.transforms object. This allows for things like using different rendering engines, custom formatting, or any other logic you might want.

Plugins run in order of registration.

The below code is used to generate this markdown file via the plugin system.

const path = require('path')
const { readFileSync } = require('fs')
const { parseComments } = require('doxxx')
const { markdownMagic } = require('../lib')
const { deepLog } = require('../lib/utils/logs')

const config = {
  matchWord: 'MD-MAGIC-EXAMPLE', // default matchWord is AUTO-GENERATED-CONTENT
  transforms: {
    /* Match <!-- AUTO-GENERATED-CONTENT:START (customTransform:optionOne=hi&optionOne=DUDE) --> */
    customTransform({ content, options }) {
      console.log('original content in comment block', content)
      console.log('options defined on transform', options)
      // options = { optionOne: hi, optionOne: DUDE}
      return `This will replace all the contents of inside the comment ${options.optionOne}`
    },
    /* Match <!-- AUTO-GENERATED-CONTENT:START JSDocs path="../file.js" --> */
    JSDocs(markdownMagicPluginAPI) {
      const { options } = markdownMagicPluginAPI
      const fileContents = readFileSync(options.path, 'utf8')
      const docBlocs = parseComments(fileContents, { skipSingleStar: true })
        .filter((item) => {
          return !item.isIgnored
        })
        /* Remove empty comments with no tags */
        .filter((item) => {
          return item.tags.length
        })
        /* Remove inline type defs */
        .filter((item) => {
          return item.description.text !== ''
        })
        /* Sort types to end */
        .sort((a, b) => {
          if (a.type && !b.type) return 1
          if (!a.type && b.type) return -1
          return 0
        })

      docBlocs.forEach((data) => {
        // console.log('data', data)
        delete data.code
      })
      // console.log('docBlocs', docBlocs)

      if (docBlocs.length === 0) {
        throw new Error('No docBlocs found')
      }

      // console.log(docBlocs.length)
      let updatedContent = ''
      docBlocs.forEach((data) => {
        if (data.type) {
          updatedContent += `#### \`${data.type}\`\n\n`
        }

        updatedContent += `${data.description.text}\n`

        if (data.tags.length) {
         let table =  '| Name | Type | Description |\n'
          table += '|:---------------------------|:---------------:|:-----------|\n'
          data.tags.filter((tag) => {
            if (tag.tagType === 'param') return true
            if (tag.tagType === 'property') return true
            return false
          }).forEach((tag) => {
            const optionalText = tag.isOptional ? ' (optional) ' : ' '
            const defaultValueText = (typeof tag.defaultValue !== 'undefined') ? ` Default: \`${tag.defaultValue}\` ` : ' '
            table += `| \`${tag.name}\`${optionalText}`
            table += `| \`${tag.type.replace('|', 'or')}\` `
            table += `| ${tag.description.replace(/\.\s?$/, '')}.${defaultValueText}|\n`
          })
          updatedContent+= `\n${table}\n`

          const returnValues = data.tags.filter((tag) => tag.tagType === 'returns')
          if (returnValues.length) {
            returnValues.forEach((returnValue) => {
              updatedContent += `**Returns**\n\n`
              updatedContent += `\`${returnValue.type}\`\n\n`
            })
          }

          const examples = data.tags.filter((tag) => tag.tagType === 'example')
          if (examples.length) {
            examples.forEach((example) => {
              updatedContent += `**Example**\n\n`
              updatedContent += `\`\`\`js\n${example.tagValue}\n\`\`\`\n\n`
            })
          }
        }
      })
      return updatedContent.replace(/^\s+|\s+$/g, '')
    },
    INLINE_EXAMPLE: () => {
      return '**⊂◉‿◉つ**'
    },
    lolz() {
      return `This section was generated by the cli config markdown.config.js file`
    },
    /* Match <!-- AUTO-GENERATED-CONTENT:START (pluginExample) --> */
    pluginExample: require('./plugin-example')({ addNewLine: true }),
    /* Include plugins from NPM */
    // count: require('markdown-magic-wordcount'),
    // github: require('markdown-magic-github-contributors')
  }
}

const markdownPath = path.join(__dirname, '..', 'README.md')
markdownMagic(markdownPath, config, () => {
  console.log('Docs ready')
})

Plugin Example

Plugins must return a transform function with the following signature.

return function myCustomTransform (content, options)
/* Custom Transform Plugin example */
module.exports = function customPlugin(pluginOptions) {
  // set plugin defaults
  const defaultOptions = {
    addNewLine: false
  }
  const userOptions = pluginOptions || {}
  const pluginConfig = Object.assign(defaultOptions, userOptions)
  // return the transform function
  return function myCustomTransform ({ content, options }) {
    const newLine = (pluginConfig.addNewLine) ? '\n' : ''
    const updatedContent = content + newLine
    return updatedContent
  }
}

View the raw file file and run npm run docs to see this plugin run

This content is altered by the pluginExample plugin registered in examples/generate-readme.js

Other usage examples

Custom Transform Demo

View the raw source of this README.md file to see the comment block and see how the customTransform function in examples/generate-readme.js works

This will replace all the contents of inside the comment DUDE

Usage examples

Misc Markdown helpers

Prior Art

This was inspired by Kent C Dodds and jfmengels's all contributors cli project.

License

MIT © DavidWells

markdown-magic's People

Contributors

arikfr avatar bradtaylorsf avatar davidwells avatar erezrokah avatar forresst avatar gr2m avatar icebob avatar plesiecki avatar rishichawda avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

markdown-magic's Issues

Allow maxdepth option in TOC

As far as I can tell, adding the maxdepth option to the TOC transform options does nothing. I would expect it to do what it does in markdown-toc.

There may be other options that are missing as well which would need further testing to find.

Latest version creates non deterministic content

We have a GitHub action in the Netlify CLI repo that verifies contributors remember to update the docs.
The action runs the docs generation (uses markdown-magic) and checks for unstaged changes.

It started failing with the latest update of this library:
https://github.com/netlify/cli/pull/2770/checks?check_run_id=2931144344

Running the docs generation script to sync the docs doesn't help.

It seems markdown-magic content generation is non deterministic.

I believe the bug was introduced in 191deed#diff-520fff59516f402ffccdf9f02539d79df6ef9064fdd72f49ffb6498ac3590fb6R93 which should be a for of to guarantee content is updated in-order.

Unicode/emoji in header anchors/IDs are broken

Anchor links that the markdown-toc plugin generates for the Toc, precent encodes any Unicode/emoji. That's all cool, but it appears that the headings strips any Unicode when creating the actual anchors. Meaning that those Toc links with Unicode in them won't work, such as those on the README.md:

e.g. the ToC has the link https://github.com/DavidWells/markdown-magic#%F0%9F%94%80-code but the anchor link is https://github.com/DavidWells/markdown-magic#-code

I haven't looked into the issue much, it could be that GFM always strips non-ascii_identifiers (I couldn't see anything in the GFM spec about it) or that it's an issue upstream with markdown-toc or something to do with this package,

Correctly replace the node_modules folder in the ignore list

This line is preventing me to exclude the common/temp/node_modules directory of my monorepo and any "subproject" node_modules directory 😢

e.g. If I md-magic --path '**/*.md' --ignore 'common/temp/node_modules/**', the tool tries to modify its own examples because it finds it in the common/temp/node_modules directory (which is the global store for every dependency of the monorepo).

Is it possible to replace it with something more robust? Maybe just use !**/node_modules/** (⚠️ if it works)?

Markdown comments are not used.

The comments used by markdown-magic are visible when converting to PDF, for example using pandoc.

Shouldn't real markdown comments be used? For example this?

BUG: doctoc breaks md-magic

Tested with npm, yarn, pnpm, global or local installation.
With node 14/16/17 and md-magic latest and older 2.5.2.

Seems like [email protected] dropped CJS support.

internal/modules/cjs/loader.js:948
    throw new ERR_REQUIRE_ESM(filename);
    ^

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /Users/ju/Library/pnpm/global/5/.pnpm/@[email protected]/node_modules/@technote-space/doctoc/dist/index.mjs
    at new NodeError (internal/errors.js:322:7)
    at Module.load (internal/modules/cjs/loader.js:948:11)
    at Function.Module._load (internal/modules/cjs/loader.js:790:12)
    at Module.require (internal/modules/cjs/loader.js:974:19)
    at require (internal/modules/cjs/helpers.js:93:18)
    at Object.<anonymous> (/Users/ju/Library/pnpm/global/5/.pnpm/[email protected]/node_modules/markdown-magic/lib/transforms/toc.js:1:23)
    at Module._compile (internal/modules/cjs/loader.js:1085:14)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
    at Module.load (internal/modules/cjs/loader.js:950:32)
    at Function.Module._load (internal/modules/cjs/loader.js:790:12) {
  code: 'ERR_REQUIRE_ESM'
}

shortant MATCHWORD

This is not so much important but it could better to make MATCHWORD short. Like >> AUTO-GENERATED-CONTENT to AGC or ~~

Example:

<!-- AGC:START (CODE:src=./relative/path/to/code.js) -->
This content will be dynamically replaced with code from the file
<!-- AGC:END -->

or

<!-- ~~:START (CODE:src=./relative/path/to/code.js) -->
This content will be dynamically replaced with code from the file
<!-- ~~:END -->

hosted version of markdownmagic

i know this is outside of mdmagic's domain but i wanted a lighter TOC generator (one where you can just copy and paste and process the TOC in-browser) and markdown magic would have been perfect for this with a few tweaks. so i tried adapting it here but wasnt able to fully remove all the fs dependencies in the time i had. just wanted to share this idea in case its something others are also looking for.

Feature Request: Override TOC transform `maxDepth` parameter in single place

In my use case I have multiple markdown files that use TOC transform, some of which have multiple nested headings requiring to specify the maxDepth parameter for those individual files.

It would be great if maxDepth could be provided for all files in a single place, e.g. in markdown.config.js file or via CLI params. This would reduce the need to specify it in every file with nested headings.

Add value from environment variable

I want to add automatic generated values from build process into my MD files (i.e. versions determined while build) without the need to implement an own extension. Referencing those values i.e. as

<!-- AUTO-GENERATED-CONTENT:START (ENV:name=myFooVariable) -->
<!-- AUTO-GENERATED-CONTENT:END -->

would allow me to add variable like

myFooVariable=42 md-magic --path './**/*.md'

Fix bun compatibility

This package is unusable with bun currently because of this package const request = require('sync-request').

I investigated the idea of moving to fetch directly, but that will destroy support for older node versions. is it possible to use an alternative fetching library? I like node-fetch @DavidWells

Feature Request: Comment range

Currently the code injection allows you to specify lines of code from a file:

 <!-- AUTO-GENERATED-CONTENT:START (CODE:src=./relative/path/to/code.js&lines=22-44) -->
 This content will be dynamically replaced with code from the file lines 22 through 44
 <!-- AUTO-GENERATED-CONTENT:END -->

This can be dificult to maintain as line numbers are subject to change with API changes. An easier way to do this would be to select between specified comment blocks. Here's a simple example:

 <!-- AUTO-GENERATED-CONTENT:START (CODE:src=./relative/path/to/code.js&block=BLOCK1) -->
 This content will be dynamically replaced with code (from the file) found between comments containing "BLOCK1".
 <!-- AUTO-GENERATED-CONTENT:END -->

code.js :

// ...

// <BLOCK1>
console.log("This will get selected");
// </BLOCK1>

Happy to help out with a PR.

REMOTE COMMAND is not quite working

Hi, I am trying to conduct a little test with some Netlify documentation. I have the following in my README.md

I am simply trying to add docs from netlify/netlify-cms to the netlify/netlify-cms-www

Any insight how to accomplish this would be greatly appreciated.

My commit/attempt is here. netlify/netlify-cms-www@39b8892

Cannot find module 'dox'

When i execute npm run docs, i have this error :

C:\markdown-magic>npm run docs

> [email protected] docs C:\markdown-magic
> node examples/generate-readme.js

original innerContent This will replace all the contents of inside the comment DUDE
{ optionOne: 'DUDE' }
module.js:472
    throw err;
    ^

Error: Cannot find module 'dox'
    at Function.Module._resolveFilename (module.js:470:15)
    at Function.Module._load (module.js:418:25)
    at Module.require (module.js:498:17)
    at require (internal/module.js:20:19)
    at Object.RENDERDOCS (C:\markdown-magic\examples\generate-readme.js:17:24)
    at updateContents (C:\markdown-magic\lib\updateContents.js:25:50)
    at transformsToRun.forEach (C:\markdown-magic\lib\processFile.js:81:26)
    at Array.forEach (native)
    at processFile (C:\markdown-magic\lib\processFile.js:79:21)
    at files.forEach (C:\markdown-magic\index.js:29:20)

npm ERR! Windows_NT 10.0.10240
npm ERR! argv "C:\\Program Files\\nodejs\\node.exe" "C:\\Users\\forresst\\AppData\\Roaming\\npm\\node_modules\\npm\\bin\\npm-cli.js" "run" "docs"
npm ERR! node v7.3.0
npm ERR! npm  v3.10.8
npm ERR! code ELIFECYCLE
npm ERR! [email protected] docs: `node examples/generate-readme.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] docs script 'node examples/generate-readme.js'.
npm ERR! Make sure you have the latest version of node.js and npm installed.
npm ERR! If you do, this is most likely a problem with the markdown-magic package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR!     node examples/generate-readme.js
npm ERR! You can get information on how to open an issue for this project with:
npm ERR!     npm bugs markdown-magic
npm ERR! Or if that isn't available, you can get their info via:
npm ERR!     npm owner ls markdown-magic
npm ERR! There is likely additional logging output above.

npm ERR! Please include the following file with any support request:
npm ERR!     C:\markdown-magic\npm-debug.log

Vulnerability in the package

Reported by npm audit

│ Moderate │ Regular Expression Denial of Service │
│ Package │ underscore.string │
│ Patched in │ >=3.3.5 │
│ Dependency of │ markdown-magic [dev] │
│ Path │ markdown-magic > markdown-toc > remarkable > argparse > │
│ │ underscore.string │
│ More info │ https://npmjs.com/advisories/745

AWS Conditional HostedZone

See Condition: CreateZone

service: netlify-ga-proxy

plugins:
  - serverless-pseudo-parameters
  - serverless-manifest-plugin

custom:
  # default stage 'prod'
  stage: ${opt:stage, 'prod'}
  # default region 'us-west-1'
  region: ${opt:region, 'us-west-1'}
  # Primary host domain
  baseDomain: cognitoguide.com
  # api.netlify-services.com/users/
  domainsByStage:
    prod:    api.${self:custom.baseDomain}
    staging: api-staging.${self:custom.baseDomain}
    dev:     api-dev.${self:custom.baseDomain}
  # Resolved domain settings
  apiDomain: ${self:custom.domainsByStage.${self:custom.stage}}
  # Base mapping. domain.com/${apiBasePath}/
  apiBasePath: 'mypath'
  # Resolved full service url
  apiServiceUrl: 'https://${self:custom.apiDomain}/${self:custom.apiBasePath}'
  # Route53 hosted zone
  hostedZoneId: Z10434111YXXDAXXD5TT0

provider:
  name: aws
  stage: ${self:custom.stage}
  region: ${self:custom.region}
  runtime: nodejs12.x
  httpApi:
    cors: true

functions:
  proxy:
    handler: handler.proxy
    events:
      - httpApi:
          method: GET
          path: /r/collect
      - httpApi:
          method: GET
          path: /collect

resources:
  Conditions:
    ## Create HostedZone if hostedZoneId empty
    CreateZone: { "Fn::Equals" : ["${self:custom.hostedZoneId, ''}", ""] }
  Resources:
    ## Hack for conditional depends clause via https://bit.ly/2X0beJL
    WaitOnHostedZone:
      Condition: CreateZone
      DependsOn: HostedZone
      Type: "AWS::CloudFormation::WaitConditionHandle"
    EmptyWait:
      Type: "AWS::CloudFormation::WaitConditionHandle"
    CustomWaitCondition:
      Type: "AWS::CloudFormation::WaitCondition"
      Properties:
        Handle: { "Fn::If": [CreateZone, { Ref: WaitOnHostedZone }, { Ref: EmptyWait }] }
        Timeout: "1"
        Count: 0
    ## https://amzn.to/3d0ZBaU
    HostedZone:
      Type: 'AWS::Route53::HostedZone'
      Condition: CreateZone
      Properties:
        HostedZoneConfig:
          Comment: 'Hosted zone for ${self:custom.baseDomain}'
        Name: '${self:custom.baseDomain}'
    Domain:
      Type: 'AWS::ApiGatewayV2::DomainName'
      DependsOn:
        - SSLCertificate
      Properties:
        DomainName: ${self:custom.apiDomain}
        DomainNameConfigurations:
          - EndpointType: REGIONAL
            CertificateArn: { Ref: SSLCertificate }
    ApiMapping:
      Type: 'AWS::ApiGatewayV2::ApiMapping'
      DependsOn:
        - HttpApi
        - HttpApiStage
        - Domain
      Properties:
        DomainName: ${self:custom.apiDomain}
        ApiId: { Ref: HttpApi }
        # What is the route
        ApiMappingKey: ${self:custom.apiBasePath}
        Stage: { Ref: HttpApiStage }
    DnsRecord:
      Type: AWS::Route53::RecordSetGroup
      Properties:
        HostedZoneName: '${self:custom.baseDomain}.'
        RecordSets:
          - Name: ${self:custom.apiDomain}
            Type: A
            AliasTarget:
              HostedZoneId: { Fn::GetAtt: [ Domain, RegionalHostedZoneId ] }
              DNSName: { Fn::GetAtt: [ Domain, RegionalDomainName ] }
    SSLCertificate:
      Type: 'Custom::DNSCertificate'
      DependsOn:
        - CustomWaitCondition
        - CustomAcmCertificateLambdaExecutionRole
        - CustomAcmCertificateLambda
      Properties:
        DomainName: '${self:custom.baseDomain}'
        SubjectAlternativeNames:
          - '*.${self:custom.baseDomain}'
        ValidationMethod: DNS
        Region: ${self:custom.region}
        DomainValidationOptions:
          - DomainName: '${self:custom.baseDomain}'
            HostedZoneId: '${self:custom.hostedZoneId}' # '#{HostedZone}'
        ServiceToken: { Fn::GetAtt: [ CustomAcmCertificateLambda, Arn ] }
    CustomAcmCertificateLambdaExecutionRole:
      Type: 'AWS::IAM::Role'
      Properties:
        AssumeRolePolicyDocument:
          Statement:
            - Action:
                - sts:AssumeRole
              Effect: Allow
              Principal:
                Service: lambda.amazonaws.com
          Version: '2012-10-17'
        ManagedPolicyArns:
          - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
          - arn:aws:iam::aws:policy/service-role/AWSLambdaRole
        Policies:
          - PolicyDocument:
              Statement:
                - Action:
                    - acm:AddTagsToCertificate
                    - acm:DeleteCertificate
                    - acm:DescribeCertificate
                    - acm:RemoveTagsFromCertificate
                  Effect: Allow
                  Resource:
                    - 'arn:aws:acm:*:${AWS::AccountId}:certificate/*'
                - Action:
                    - acm:RequestCertificate
                    - acm:ListTagsForCertificate
                    - acm:ListCertificates
                  Effect: Allow
                  Resource:
                    - '*'
                - Action:
                    - route53:ChangeResourceRecordSets
                  Effect: Allow
                  Resource:
                    - arn:aws:route53:::hostedzone/*
              Version: '2012-10-17'
            PolicyName: 'CustomAcmCertificateLambdaExecutionPolicy-${self:service}'
    CustomAcmCertificateLambda:
      Type: 'AWS::Lambda::Function'
      Metadata:
        Source: https://github.com/dflook/cloudformation-dns-certificate
        Version: 1.7.2
      Properties:
        Description: Cloudformation custom resource for DNS validated certificates
        Handler: index.handler
        Role: { Fn::GetAtt: [ CustomAcmCertificateLambdaExecutionRole, Arn ] }
        Runtime: python3.6
        Timeout: 900
        Code:
          ZipFile: "y=Exception\nU=RuntimeError\nM=True\nimport copy,hashlib as t,json,logging\
            \ as B,time\nfrom boto3 import client as J\nfrom botocore.exceptions import\
            \ ClientError as u,ParamValidationError as v\nfrom urllib.request import\
            \ Request as w,urlopen as x\nA=B.getLogger()\nA.setLevel(B.INFO)\nC=A.info\n\
            R=A.exception\nL=copy.copy\nS=time.sleep\nT=lambda j:json.dumps(j,sort_keys=M).encode()\n\
            def handler(e,c):\n\tAB='OldResourceProperties';AA='Update';A9='Delete';A8='None';A7='acm';A6='FAILED';A5='properties';A4='stack-id';A3='logical-id';A2='DNS';s='Old';r='Certificate';q='LogicalResourceId';p='DomainName';o='ValidationMethod';n='Route53RoleArn';m='Region';d='RequestType';b='Reinvoked';a='StackId';Z=None;Q='Status';P='Key';O='';N='DomainValidationOptions';K=False;I='ResourceProperties';H='cloudformation:';G='Value';F='CertificateArn';E='Tags';B='PhysicalResourceId';f=c.get_remaining_time_in_millis;C(e)\n\
            \tdef g():\n\t\tC=L(A)\n\t\tfor G in ['ServiceToken',m,E,n]:C.pop(G,Z)\n\
            \t\tif o in A:\n\t\t\tif A[o]==A2:\n\t\t\t\tfor H in set([A[p]]+A.get('SubjectAlternativeNames',[])):k(H)\n\
            \t\t\t\tdel C[N]\n\t\te[B]=D.request_certificate(IdempotencyToken=A0,**C)[F];l()\n\
            \tdef V(a):\n\t\twhile M:\n\t\t\ttry:D.delete_certificate(**{F:a});return\n\
            \t\t\texcept u as B:\n\t\t\t\tR(O);A=B.response['Error']['Code']\n\t\t\t\
            \tif A=='ResourceInUseException':\n\t\t\t\t\tif f()/1000<30:raise\n\t\t\t\
            \t\tS(5);continue\n\t\t\t\tif A in['ResourceNotFoundException','ValidationException']:return\n\
            \t\t\t\traise\n\t\t\texcept v:return\n\tdef W(p):\n\t\tfor I in D.get_paginator('list_certificates').paginate():\n\
            \t\t\tfor A in I['CertificateSummaryList']:\n\t\t\t\tC(A);B={B[P]:B[G]for\
            \ B in D.list_tags_for_certificate(**{F:A[F]})[E]}\n\t\t\t\tif B.get(H+A3)==e[q]and\
            \ B.get(H+A4)==e[a]and B.get(H+A5)==X(p):return A[F]\n\tdef h():\n\t\tif\
            \ e.get(b,K):raise U('Certificate not issued in time')\n\t\te[b]=M;C(e);J('lambda').invoke(FunctionName=c.invoked_function_arn,InvocationType='Event',Payload=T(e))\n\
            \tdef i():\n\t\twhile f()/1000>30:\n\t\t\tA=D.describe_certificate(**{F:e[B]})[r];C(A)\n\
            \t\t\tif A[Q]=='ISSUED':return M\n\t\t\telif A[Q]==A6:raise U(A.get('FailureReason',O))\n\
            \t\t\tS(5)\n\t\treturn K\n\tdef z():A=L(e[s+I]);A.pop(E,Z);B=L(e[I]);B.pop(E,Z);return\
            \ A!=B\n\tdef j():\n\t\tW='Type';V='Name';U='HostedZoneId';T='ValidationStatus';R='PENDING_VALIDATION';K='ResourceRecord'\n\
            \t\tif A.get(o)!=A2:return\n\t\twhile M:\n\t\t\tH=D.describe_certificate(**{F:e[B]})[r];C(H)\n\
            \t\t\tif H[Q]!=R:return\n\t\t\tif not[A for A in H.get(N,[{}])if T not in\
            \ A or K not in A]:break\n\t\t\tS(1)\n\t\tfor E in H[N]:\n\t\t\tif E[T]==R:L=k(E[p]);O=L.get(n,A.get(n));I=J('sts').assume_role(RoleArn=O,RoleSessionName=(r+e[q])[:64],DurationSeconds=900)['Credentials']if\
            \ O is not Z else{};P=J('route53',aws_access_key_id=I.get('AccessKeyId'),aws_secret_access_key=I.get('SecretAccessKey'),aws_session_token=I.get('SessionToken')).change_resource_record_sets(**{U:L[U],'ChangeBatch':{'Comment':'Domain\
            \ validation for '+e[B],'Changes':[{'Action':'UPSERT','ResourceRecordSet':{V:E[K][V],W:E[K][W],'TTL':60,'ResourceRecords':[{G:E[K][G]}]}}]}});C(P)\n\
            \tdef k(n):\n\t\tC='.';n=n.rstrip(C);D={B[p].rstrip(C):B for B in A[N]};B=n.split(C)\n\
            \t\twhile len(B):\n\t\t\tif C.join(B)in D:return D[C.join(B)]\n\t\t\tB=B[1:]\n\
            \t\traise U(N+' missing for '+n)\n\tX=lambda v:t.new('md5',T(v)).hexdigest()\n\
            \tdef l():A=L(e[I].get(E,[]));A+=[{P:H+A3,G:e[q]},{P:H+A4,G:e[a]},{P:H+'stack-name',G:e[a].split('/')[1]},{P:H+A5,G:X(e[I])}];D.add_tags_to_certificate(**{F:e[B],E:A})\n\
            \tdef Y():\n\t\tC(e);A=x(w(e['ResponseURL'],T(e),{'content-type':O},method='PUT'))\n\
            \t\tif A.status!=200:raise y(A)\n\ttry:\n\t\tA0=X(e['RequestId']+e[a]);A=e[I];D=J(A7,region_name=A.get(m));e[Q]='SUCCESS'\n\
            \t\tif e[d]=='Create':\n\t\t\tif e.get(b,K)is K:e[B]=A8;g()\n\t\t\tj()\n\
            \t\t\tif not i():return h()\n\t\telif e[d]==A9:\n\t\t\tif e[B]!=A8:\n\t\t\
            \t\tif e[B].startswith('arn:'):V(e[B])\n\t\t\t\telse:V(W(A))\n\t\telif e[d]==AA:\n\
            \t\t\tif z():\n\t\t\t\tC(AA)\n\t\t\t\tif W(A)==e[B]:\n\t\t\t\t\ttry:D=J(A7,region_name=e[AB].get(m));C(A9);V(W(e[AB]))\n\
            \t\t\t\t\texcept:R(O)\n\t\t\t\t\treturn Y()\n\t\t\t\tif e.get(b,K)is K:g()\n\
            \t\t\t\tj()\n\t\t\t\tif not i():return h()\n\t\t\telse:\n\t\t\t\tif E in\
            \ e[s+I]:D.remove_tags_from_certificate(**{F:e[B],E:e[s+I][E]})\n\t\t\t\t\
            l()\n\t\telse:raise U(e[d])\n\t\treturn Y()\n\texcept y as A1:R(O);e[Q]=A6;e['Reason']=str(A1);return\
            \ Y()"
  Outputs:
    DomainName:
      Description: API Gateway custom domain
      Value: { Ref: Domain }
    ServiceUrl:
      Description: Custom API Gateway service URL
      Value: ${self:custom.apiServiceUrl}
    DomainMapping:
      Description: Apigateway mapping
      Value: { Ref: ApiMapping }
    CertificateArn:
      Description: ARN of custom domain cert
      Value: { Ref: SSLCertificate }

Optional syntax with CODE transform is not detected when using lines

When using the optional parameter lines, then the optional syntax will not be interepreted anymore and no syntax will be placed. I would expect ```js ... in the example below but nothing happens.

<!-- AUTO-GENERATED-CONTENT:START (CODE:src=./relative/path/to/code.js&lines=22-44) -->
This content will be dynamically replaced with code from the file lines 22 through 44
<!-- AUTO-GENERATED-CONTENT:END -->

Docu states: syntax (optional): Syntax will be inferred by fileType if not specified

Module not found: Error: Can't resolve 'fs' in

ERROR in ./node_modules/glob/glob.js
Module not found: Error: Can't resolve 'fs' in '/Users/zhangwei/Development/com.github/react-code-previewer/node_modules/glob'
 @ ./node_modules/glob/glob.js 43:9-22
 @ ./node_modules/globby/index.js
 @ ./node_modules/markdown-magic/index.js
 @ ./components/code-previewer/index.js
 @ ./src/pages/CodePreviewerPage.js
 @ ./src/App.js
 @ ./src/index.js

Transformation: current file name and exception handling

Is there a way to determine the current file being processed within a transform? I would like to be able to do error reporting when an exception occurs within a transform, and in this error reporting mention the file being processed.

Fetch Range of code

Hi, I filtered the open and closed issues and did not come across this feature request that I am curious about.
I am currently getting code from URL and put in markdown with markdown-magic.

Idea:
We have big files of code in our public git repository, and I do not want to create gists from each particular method or function of interest (because I want to avoid to maintain multiple files) in order to show only a partial code snippet in markdown.

I would not know directly how to use the custom transforms but it would be cool to have the ability the define a range with Lines of Code which will then be replaced with code from the file.

Are there any implementations where this is done already?

Kind regards,
Tom

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.