mirror of
https://github.com/zhigang1992/docz.git
synced 2026-03-26 10:14:33 +08:00
Merge branch 'dev' of github.com:pedronauck/docz into dev
* 'dev' of github.com:pedronauck/docz: fix(docz-core): data server rewriting files and refac utils (#370) fix(docz-theme-default): use local vendors (#356) feat(docz-core): add --config flag (#367) docs: fix downloads badge (#346)
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
<p align="center">
|
||||
<img src="https://badgen.net/npm/v/docz" alt="">
|
||||
<img src="https://badgen.net/badge/license/MIT/blue" alt="">
|
||||
<img src="https://badgen.net/npm/dt/express" alt="">
|
||||
<img src="https://badgen.net/npm/dt/docz" alt="">
|
||||
<a href="https://discord.gg/YQE4MbD">
|
||||
<img src="https://img.shields.io/badge/chat-on%20discord-7289da.svg" alt="Chat">
|
||||
</a>
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
"cpy": "^7.0.1",
|
||||
"deepmerge": "^2.1.1",
|
||||
"detect-port": "^1.2.3",
|
||||
"docz-utils": "^0.11.2",
|
||||
"dotenv": "^6.0.0",
|
||||
"env-dot-prop": "^1.0.2",
|
||||
"fast-deep-equal": "^2.0.1",
|
||||
@@ -63,7 +64,6 @@
|
||||
"load-cfg": "^0.11.1",
|
||||
"lodash.get": "^4.4.2",
|
||||
"mini-html-webpack-plugin": "^0.2.3",
|
||||
"prettier": "^1.14.2",
|
||||
"react-dev-utils": "^5.0.2",
|
||||
"react-docgen-typescript-loader": "^3.0.0-rc.0",
|
||||
"react-hot-loader": "4.3.8",
|
||||
@@ -72,17 +72,10 @@
|
||||
"remark-docz": "^0.11.0",
|
||||
"remark-frontmatter": "^1.2.1",
|
||||
"remark-parse": "^5.0.0",
|
||||
"remark-parse-yaml": "^0.0.1",
|
||||
"remark-slug": "^5.1.0",
|
||||
"resolve": "^1.8.1",
|
||||
"signale": "^1.3.0",
|
||||
"terser-webpack-plugin": "^1.1.0",
|
||||
"titleize": "^1.0.1",
|
||||
"to-vfile": "^5.0.1",
|
||||
"unified": "^7.0.0",
|
||||
"unist-util-find": "^1.0.1",
|
||||
"unist-util-is": "^2.1.2",
|
||||
"unist-util-visit": "^1.4.0",
|
||||
"terser-webpack-plugin": "^1.1.0",
|
||||
"url-loader": "^1.1.1",
|
||||
"webpack": "^4.19.1",
|
||||
"webpack-chain": "^4.11.0",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as path from 'path'
|
||||
import * as fs from 'fs-extra'
|
||||
import { parseMdx } from 'docz-utils/lib/mdast'
|
||||
import glob from 'fast-glob'
|
||||
|
||||
import * as paths from './config/paths'
|
||||
@@ -9,11 +10,9 @@ import { mapToObj } from './utils/helpers'
|
||||
import { Entry, EntryObj } from './Entry'
|
||||
import { Plugin } from './Plugin'
|
||||
import { Config } from './commands/args'
|
||||
import { parseMdx } from './utils/ast'
|
||||
import { getRepoEditUrl } from './utils/repo-info'
|
||||
|
||||
const DEFAULT_IGNORE = [
|
||||
'!**/node_modules/**',
|
||||
'readme.md',
|
||||
'changelog.md',
|
||||
'code_of_conduct.md',
|
||||
@@ -21,6 +20,9 @@ const DEFAULT_IGNORE = [
|
||||
'license.md',
|
||||
]
|
||||
|
||||
const ignoreFiles = (arr: string[]) =>
|
||||
['!**/node_modules/**'].concat(arr.length > 0 ? arr : DEFAULT_IGNORE)
|
||||
|
||||
export const fromTemplates = (file: string) => path.join(paths.templates, file)
|
||||
|
||||
const matchFilesWithSrc = (config: Config) => (files: string[]) => {
|
||||
@@ -83,7 +85,7 @@ export class Entries {
|
||||
const toMatch = matchFilesWithSrc(config)
|
||||
|
||||
const files = await glob<string>(toMatch(arr), {
|
||||
ignore: DEFAULT_IGNORE.concat(ignore),
|
||||
ignore: ignoreFiles(ignore),
|
||||
onlyFiles: true,
|
||||
unique: true,
|
||||
nocase: true,
|
||||
|
||||
@@ -2,45 +2,14 @@ import * as path from 'path'
|
||||
import * as crypto from 'crypto'
|
||||
import slugify from '@sindresorhus/slugify'
|
||||
import humanize from 'humanize-string'
|
||||
import find from 'unist-util-find'
|
||||
import is from 'unist-util-is'
|
||||
import visit from 'unist-util-visit'
|
||||
import get from 'lodash.get'
|
||||
import {
|
||||
Heading,
|
||||
getParsedData,
|
||||
headingsFromAst,
|
||||
ParsedData,
|
||||
} from 'docz-utils/lib/mdast'
|
||||
|
||||
import * as paths from './config/paths'
|
||||
import { valueFromHeading } from './utils/ast'
|
||||
|
||||
interface ParsedData {
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
const getParsedData = (ast: any): ParsedData => {
|
||||
const node = find(ast, (node: any) => is('yaml', node))
|
||||
return get(node, `data.parsedValue`) || {}
|
||||
}
|
||||
|
||||
export interface Heading {
|
||||
depth: number
|
||||
slug: string
|
||||
value: string
|
||||
}
|
||||
|
||||
const getHeadings = (ast: any): Heading[] => {
|
||||
const headings: Heading[] = []
|
||||
|
||||
visit(ast, 'heading', (node: any) => {
|
||||
const slug = get(node, 'data.id')
|
||||
const depth = get(node, 'depth')
|
||||
|
||||
headings.push({
|
||||
depth,
|
||||
slug,
|
||||
value: valueFromHeading(node),
|
||||
})
|
||||
})
|
||||
|
||||
return headings
|
||||
}
|
||||
|
||||
const createId = (file: string) =>
|
||||
crypto
|
||||
@@ -90,7 +59,7 @@ export class Entry {
|
||||
this.name = name
|
||||
this.order = parsed.order || 0
|
||||
this.menu = parsed.menu || null
|
||||
this.headings = getHeadings(ast)
|
||||
this.headings = headingsFromAst(ast)
|
||||
this.settings = parsed
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ export interface Argv {
|
||||
ignore: string[]
|
||||
dest: string
|
||||
editBranch: string
|
||||
config: string
|
||||
/* bundler args */
|
||||
debug: boolean
|
||||
typescript: boolean
|
||||
@@ -122,7 +123,10 @@ export const args = (env: Env) => (yargs: any) => {
|
||||
type: 'string',
|
||||
default: getEnv('docz.edit.branch', 'master'),
|
||||
})
|
||||
|
||||
yargs.positional('config', {
|
||||
type: 'string',
|
||||
default: getEnv('docz.config', ''),
|
||||
})
|
||||
yargs.positional('title', {
|
||||
type: 'string',
|
||||
default: getEnv('docz.title', getInitialTitle(pkg)),
|
||||
|
||||
@@ -12,13 +12,15 @@ import { Config } from './args'
|
||||
export const build = async (args: Config) => {
|
||||
const env = envDotProp.get('node.env')
|
||||
const config = loadConfig(args)
|
||||
const entries = new Entries(config)
|
||||
|
||||
const bundler = webpack(config, env)
|
||||
const run = Plugin.runPluginsMethod(config.plugins)
|
||||
const dataServer = new DataServer()
|
||||
|
||||
try {
|
||||
dataServer.register([states.entries(config), states.config(config)])
|
||||
dataServer.register([states.config(config), states.entries(entries, config)])
|
||||
|
||||
try {
|
||||
await Entries.writeApp(config)
|
||||
await dataServer.init()
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ export const dev = async (args: Config) => {
|
||||
|
||||
const newConfig = { ...config, websocketPort, hotPort, port }
|
||||
const bundler = webpack(newConfig, env)
|
||||
const entries = new Entries(config)
|
||||
|
||||
const bundlerConfig = await bundler.getConfig(env)
|
||||
const server = await bundler.createServer(bundlerConfig)
|
||||
const { app } = await server.start()
|
||||
@@ -32,9 +34,12 @@ export const dev = async (args: Config) => {
|
||||
config.websocketHost
|
||||
)
|
||||
|
||||
try {
|
||||
dataServer.register([states.entries(newConfig), states.config(newConfig)])
|
||||
dataServer.register([
|
||||
states.config(newConfig),
|
||||
states.entries(entries, newConfig),
|
||||
])
|
||||
|
||||
try {
|
||||
await Entries.writeApp(newConfig, true)
|
||||
await dataServer.init()
|
||||
await dataServer.listen()
|
||||
|
||||
@@ -3,10 +3,12 @@ import slug from 'rehype-slug'
|
||||
import remarkDocz from 'remark-docz'
|
||||
import rehypeDocz from 'rehype-docz'
|
||||
|
||||
import * as paths from './paths'
|
||||
|
||||
export const config = {
|
||||
type: 'yaml',
|
||||
marker: '-',
|
||||
}
|
||||
|
||||
export const remarkPlugins = [matter, remarkDocz]
|
||||
export const rehypePlugins = [rehypeDocz, slug]
|
||||
export const rehypePlugins = [rehypeDocz(paths.root), slug]
|
||||
|
||||
@@ -25,8 +25,7 @@ const updateEntries = (entries: Entries) => async (p: Params) => {
|
||||
}
|
||||
}
|
||||
|
||||
export const state = (config: Config): State => {
|
||||
const entries = new Entries(config)
|
||||
export const state = (entries: Entries, config: Config): State => {
|
||||
const src = path.relative(paths.root, config.src)
|
||||
const files = path.join(src, config.files)
|
||||
const watcher = chokidar.watch(files, {
|
||||
|
||||
8
packages/docz-core/src/types.d.ts
vendored
8
packages/docz-core/src/types.d.ts
vendored
@@ -9,25 +9,17 @@ declare module 'find-up'
|
||||
declare module 'humanize-string'
|
||||
declare module 'get-pkg-repo'
|
||||
declare module 'titleize'
|
||||
declare module 'unified'
|
||||
declare module 'strip-indent'
|
||||
declare module 'unist-util-is'
|
||||
declare module 'unist-util-visit'
|
||||
declare module 'unist-util-find'
|
||||
declare module 'unist-util-remove'
|
||||
declare module 'hast-util-to-string'
|
||||
declare module 'html-minifer'
|
||||
declare module 'mini-html-webpack-plugin'
|
||||
declare module 'happypack'
|
||||
declare module 'common-tags'
|
||||
declare module 'remark-parse'
|
||||
declare module 'remark-slug'
|
||||
declare module 'remark-parse-yaml'
|
||||
declare module 'remark-frontmatter'
|
||||
declare module 'remark-parse/lib/block-elements.json'
|
||||
declare module 'rehype-slug'
|
||||
declare module 'rehype-autolink-headings'
|
||||
declare module 'to-vfile'
|
||||
declare module 'art-template'
|
||||
declare module 'friendly-errors-webpack-plugin'
|
||||
declare module 'webpack-manifest-plugin'
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import * as path from 'path'
|
||||
import * as fs from 'fs-extra'
|
||||
import { compile } from 'art-template'
|
||||
|
||||
import { format } from './format'
|
||||
import { format } from 'docz-utils'
|
||||
|
||||
export const touch = (file: string, raw: string) =>
|
||||
new Promise(async (resolve, reject) => {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { load } from 'load-cfg'
|
||||
import * as path from 'path'
|
||||
|
||||
import { load, loadFrom } from 'load-cfg'
|
||||
|
||||
import * as paths from '../config/paths'
|
||||
import { Config } from '../commands/args'
|
||||
@@ -13,7 +15,7 @@ const defaultHtmlContext = {
|
||||
}
|
||||
|
||||
export const loadConfig = (args: Config): Config => {
|
||||
const config = load<Config>('docz', {
|
||||
const defaultConfig = {
|
||||
...args,
|
||||
hashRouter: false,
|
||||
native: false,
|
||||
@@ -25,7 +27,11 @@ export const loadConfig = (args: Config): Config => {
|
||||
menu: [],
|
||||
modifyBundlerConfig: (config: any) => config,
|
||||
modifyBabelRc: (babelrc: BabelRC) => babelrc,
|
||||
})
|
||||
}
|
||||
|
||||
const config = args.config
|
||||
? loadFrom<Config>(path.resolve(args.config), defaultConfig)
|
||||
: load<Config>('docz', defaultConfig)
|
||||
|
||||
const reduce = Plugin.reduceFromPlugins<Config>(config.plugins)
|
||||
return omit<Config>(toOmit, reduce('setConfig', { ...config, paths }))
|
||||
|
||||
@@ -74,7 +74,6 @@ export const parseHtml = ({ config, ctx, dev, template }: ParseHtmlParams) => {
|
||||
} = ctx
|
||||
|
||||
const headStr = `
|
||||
<link rel="stylesheet" type="text/css" href="https://unpkg.com/codemirror@5.39.2/lib/codemirror.css" />
|
||||
${favicon ? `<link rel="icon" type="image/x-icon" href="${favicon}">` : ''}
|
||||
${head.meta ? generateMetaTags(head.meta) : ''}
|
||||
${head.links ? generateLinkTags(head.links) : ''}
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"lodash.flattendepth": "^4.7.0",
|
||||
"lodash.get": "^4.4.2",
|
||||
"match-sorter": "^2.3.0",
|
||||
"normalize.css": "^8.0.0",
|
||||
"polished": "^2.0.3",
|
||||
"pretty": "^2.0.0",
|
||||
"prop-types": "15.6.2",
|
||||
@@ -59,8 +60,10 @@
|
||||
"@types/react-dom": "^16.0.6",
|
||||
"babel-loader": "^8.0.2",
|
||||
"babel-plugin-emotion": "^9.2.9",
|
||||
"css-loader": "^1.0.0",
|
||||
"cross-env": "^5.2.0",
|
||||
"filemanager-webpack-plugin": "^2.0.5",
|
||||
"style-loader": "^0.23.0",
|
||||
"terser-webpack-plugin": "^1.1.0",
|
||||
"ts-loader": "^5.1.1",
|
||||
"webpack": "^4.19.1",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { injectGlobal } from 'emotion'
|
||||
import 'normalize.css'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
|
||||
// tslint:disable
|
||||
injectGlobal`
|
||||
@import url('https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css');
|
||||
|
||||
*, *:before, *:after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ const externalList = [
|
||||
'codemirror/addon/edit/matchbrackets',
|
||||
'codemirror/addon/edit/closetag',
|
||||
'codemirror/addon/fold/xml-fold',
|
||||
'normalize.css',
|
||||
'react-perfect-scrollbar',
|
||||
'polished/lib/color/rgba',
|
||||
'polished/lib/color/lighten',
|
||||
@@ -40,10 +41,15 @@ const externalList = [
|
||||
'react-sizes',
|
||||
]
|
||||
|
||||
const internals = [
|
||||
'normalize.css',
|
||||
'codemirror/lib/codemirror.css'
|
||||
]
|
||||
|
||||
const deps = Object.keys(pkg.dependencies)
|
||||
const externals = Object.keys(pkg.dependencies)
|
||||
const externals = deps
|
||||
.concat(externalList)
|
||||
.concat(deps.filter(dep => dep.startsWith('react-feather')))
|
||||
.filter(dep => internals.indexOf(dep) === -1)
|
||||
|
||||
const minify = new TerserPlugin({
|
||||
terserOptions: {
|
||||
@@ -107,6 +113,10 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [ 'style-loader', 'css-loader' ]
|
||||
}
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
|
||||
53
packages/docz-utils/package.json
Normal file
53
packages/docz-utils/package.json
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "docz-utils",
|
||||
"version": "0.11.2",
|
||||
"description": "Some methods used and utilities used on docz",
|
||||
"license": "MIT",
|
||||
"main": "lib/index.js",
|
||||
"module": "lib/index.m.js",
|
||||
"typings": "lib/index.d.ts",
|
||||
"source": [
|
||||
"src/index.ts",
|
||||
"src/ast.ts",
|
||||
"src/mdast.ts",
|
||||
"src/codesandbox.ts",
|
||||
"src/format.ts",
|
||||
"src/imports.ts",
|
||||
"src/jsx.ts"
|
||||
],
|
||||
"files": [
|
||||
"lib/",
|
||||
"package.json",
|
||||
"README.md"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "libundler watch --dest lib --ts -e all",
|
||||
"build": "libundler build --dest lib --ts -e all --c",
|
||||
"fix": "run-s fix:*",
|
||||
"fix:prettier": "prettier \"src/**/*.{ts,tsx}\" --write",
|
||||
"fix:tslint": "tslint --fix --project .",
|
||||
"tslint": "tslint --project ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/generator": "^7.0.0",
|
||||
"@babel/parser": "^7.1.0",
|
||||
"@babel/traverse": "^7.1.0",
|
||||
"codesandboxer-fs": "^0.3.1",
|
||||
"fs-extra": "^7.0.0",
|
||||
"humanize-string": "^1.0.2",
|
||||
"jsx-ast-utils": "^2.0.1",
|
||||
"lodash.get": "^4.4.2",
|
||||
"prettier": "^1.14.3",
|
||||
"remark-frontmatter": "^1.2.1",
|
||||
"remark-parse": "^5.0.0",
|
||||
"remark-parse-yaml": "^0.0.1",
|
||||
"remark-slug": "^5.1.0",
|
||||
"signale": "^1.3.0",
|
||||
"strip-indent": "^2.0.0",
|
||||
"to-vfile": "^5.0.1",
|
||||
"unified": "^7.0.0",
|
||||
"unist-util-find": "^1.0.1",
|
||||
"unist-util-is": "^2.1.2",
|
||||
"unist-util-visit": "^1.4.0"
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,19 @@ import * as parser from '@babel/parser'
|
||||
import traverse from '@babel/traverse'
|
||||
|
||||
type Condition = (path: any) => boolean
|
||||
type Predicate = (path: any) => any
|
||||
|
||||
export const codeFromNode = (condition: Condition) => (code: string) => {
|
||||
export const valueFromTraverse = (
|
||||
condition: Condition,
|
||||
predicate: Predicate = p => p
|
||||
) => (code: string) => {
|
||||
let value = ''
|
||||
const ast = parser.parse(code, { plugins: ['jsx'] })
|
||||
|
||||
traverse(ast, {
|
||||
enter(path: any): void {
|
||||
if (condition(path)) {
|
||||
value = code.slice(path.node.start, path.node.end)
|
||||
value = predicate(path)
|
||||
path.stop()
|
||||
return
|
||||
}
|
||||
@@ -19,3 +23,6 @@ export const codeFromNode = (condition: Condition) => (code: string) => {
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
export const codeFromNode = (condition: Condition) => (code: string) =>
|
||||
valueFromTraverse(condition, p => code.slice(p.node.start, p.node.end))(code)
|
||||
@@ -1,5 +1,5 @@
|
||||
import { assembleFiles } from 'codesandboxer-fs'
|
||||
import * as path from 'path'
|
||||
import { assembleFiles } from 'codesandboxer-fs'
|
||||
|
||||
const wrapCode = (code: string): string => {
|
||||
return `import React from 'react';
|
||||
@@ -24,7 +24,7 @@ export default () => (
|
||||
)`
|
||||
}
|
||||
|
||||
export function getCodeSandboxFiles(
|
||||
function getSandboxFiles(
|
||||
code: string,
|
||||
imports: string[],
|
||||
cwd: string
|
||||
@@ -37,3 +37,20 @@ export function getCodeSandboxFiles(
|
||||
contents: rawCode,
|
||||
})
|
||||
}
|
||||
|
||||
export const getSandboxImportInfo = async (
|
||||
child: any,
|
||||
imports: string[],
|
||||
cwd: string
|
||||
) => {
|
||||
let info: string | undefined
|
||||
|
||||
try {
|
||||
const { parameters } = await getSandboxFiles(child, imports, cwd)
|
||||
info = parameters
|
||||
} catch (e) {
|
||||
console.error('Could not create Open in CodeSandbox', e)
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
39
packages/docz-utils/src/imports.ts
Normal file
39
packages/docz-utils/src/imports.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import * as parser from '@babel/parser'
|
||||
import * as generator from '@babel/generator'
|
||||
import traverse from '@babel/traverse'
|
||||
import get from 'lodash.get'
|
||||
|
||||
const fromSpecifiers = (specifiers: any = []) =>
|
||||
Array.isArray(specifiers) && specifiers.length > 0
|
||||
? specifiers.map(specifier => get(specifier, 'local.name'))
|
||||
: []
|
||||
|
||||
const traverseOnImports = (fn: (path: any) => any[]) => (node: any) => {
|
||||
try {
|
||||
const ast = parser.parse(node.value, { sourceType: 'module' })
|
||||
let populated: any[] = []
|
||||
|
||||
traverse(ast, {
|
||||
enter(path: any): void {
|
||||
if (path.isImportDeclaration()) {
|
||||
if (get(path, 'node.source.value') !== 'docz') {
|
||||
populated = populated.concat(fn(path))
|
||||
}
|
||||
return
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
return populated
|
||||
} catch (err) {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export const getFullImports = traverseOnImports((path: any) => [
|
||||
get(generator.default(path.node), 'code'),
|
||||
])
|
||||
|
||||
export const getImportsVariables = traverseOnImports((path: any) =>
|
||||
fromSpecifiers(path.node.specifiers)
|
||||
)
|
||||
8
packages/docz-utils/src/index.ts
Normal file
8
packages/docz-utils/src/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import * as ast from './ast'
|
||||
import * as mdast from './mdast'
|
||||
import * as jsx from './jsx'
|
||||
import * as imports from './imports'
|
||||
import * as codesandbox from './codesandbox'
|
||||
import { format } from './format'
|
||||
|
||||
export { ast, mdast, format, imports, jsx, codesandbox }
|
||||
28
packages/docz-utils/src/jsx.ts
Normal file
28
packages/docz-utils/src/jsx.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import * as jsxUtils from 'jsx-ast-utils'
|
||||
import strip from 'strip-indent'
|
||||
|
||||
import { valueFromTraverse, codeFromNode } from './ast'
|
||||
|
||||
export const propFromElement = (prop: string) =>
|
||||
valueFromTraverse(
|
||||
p => p.isJSXOpeningElement(),
|
||||
p => jsxUtils.getPropValue(jsxUtils.getProp(p.node.attributes, prop))
|
||||
)
|
||||
|
||||
export const removeTags = (code: string) => {
|
||||
const open = codeFromNode(p => p.isJSXOpeningElement())
|
||||
const close = codeFromNode(p => p.isJSXClosingElement())
|
||||
|
||||
return code.replace(open(code), '').replace(close(code), '')
|
||||
}
|
||||
|
||||
export const sanitizeCode = (code: string) =>
|
||||
strip(code)
|
||||
.trim()
|
||||
.replace(/'/g, `\\'`)
|
||||
.replace(/`/g, '\\`')
|
||||
|
||||
export const componentName = (value: any) => {
|
||||
const match = value.match(/^\<\\?(\w+)/)
|
||||
return match && match[1]
|
||||
}
|
||||
@@ -4,6 +4,9 @@ import remark from 'remark-parse'
|
||||
import matter from 'remark-frontmatter'
|
||||
import slug from 'remark-slug'
|
||||
import parseFrontmatter from 'remark-parse-yaml'
|
||||
import find from 'unist-util-find'
|
||||
import is from 'unist-util-is'
|
||||
import visit from 'unist-util-visit'
|
||||
import humanize from 'humanize-string'
|
||||
import get from 'lodash.get'
|
||||
|
||||
@@ -18,7 +21,7 @@ export const parseMdx = (file: string): Promise<string> => {
|
||||
return parser.run(parser.parse(raw))
|
||||
}
|
||||
|
||||
export const valueFromHeading = (node: any): string => {
|
||||
const valueFromHeading = (node: any): string => {
|
||||
const children = get(node, 'children')
|
||||
const slug = get(node, 'data.id')
|
||||
|
||||
@@ -31,3 +34,35 @@ export const valueFromHeading = (node: any): string => {
|
||||
|
||||
return humanize(slug)
|
||||
}
|
||||
|
||||
export interface Heading {
|
||||
depth: number
|
||||
slug: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export const headingsFromAst = (ast: any): Heading[] => {
|
||||
const headings: Heading[] = []
|
||||
|
||||
visit(ast, 'heading', (node: any) => {
|
||||
const slug = get(node, 'data.id')
|
||||
const depth = get(node, 'depth')
|
||||
|
||||
headings.push({
|
||||
depth,
|
||||
slug,
|
||||
value: valueFromHeading(node),
|
||||
})
|
||||
})
|
||||
|
||||
return headings
|
||||
}
|
||||
|
||||
export interface ParsedData {
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
export const getParsedData = (ast: any): ParsedData => {
|
||||
const node = find(ast, (node: any) => is('yaml', node))
|
||||
return get(node, `data.parsedValue`) || {}
|
||||
}
|
||||
18
packages/docz-utils/src/types.d.ts
vendored
Normal file
18
packages/docz-utils/src/types.d.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
declare module 'deepmerge'
|
||||
declare module 'to-vfile'
|
||||
declare module 'unified'
|
||||
declare module 'remark-parse'
|
||||
declare module 'remark-frontmatter'
|
||||
declare module 'remark-slug'
|
||||
declare module 'remark-parse-yaml'
|
||||
declare module 'humanize-string'
|
||||
declare module 'signale'
|
||||
declare module '@babel/parser'
|
||||
declare module '@babel/traverse'
|
||||
declare module '@babel/generator'
|
||||
declare module 'jsx-ast-utils'
|
||||
declare module 'strip-indent'
|
||||
declare module 'unist-util-visit'
|
||||
declare module 'unist-util-is'
|
||||
declare module 'unist-util-find'
|
||||
declare module 'codesandboxer-fs'
|
||||
12
packages/docz-utils/tsconfig.json
Normal file
12
packages/docz-utils/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"declaration": true,
|
||||
"types": ["node"],
|
||||
"typeRoots": ["node_modules/@types"]
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules/**"]
|
||||
}
|
||||
3
packages/docz-utils/tslint.json
Normal file
3
packages/docz-utils/tslint.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../../tslint.json"
|
||||
}
|
||||
@@ -63,3 +63,19 @@ export function load<C = any>(
|
||||
: Object.assign({}, defaultConfig, file)
|
||||
: file
|
||||
}
|
||||
|
||||
export function loadFrom<C = any>(
|
||||
filePath: string,
|
||||
defaultConfig: C,
|
||||
noCache?: boolean,
|
||||
deep?: boolean
|
||||
): C {
|
||||
const file = loadFile(filePath, noCache)
|
||||
|
||||
// tslint:disable
|
||||
return defaultConfig
|
||||
? deep
|
||||
? merge(defaultConfig, file)
|
||||
: Object.assign({}, defaultConfig, file)
|
||||
: file
|
||||
}
|
||||
|
||||
@@ -22,20 +22,13 @@
|
||||
"tslint": "tslint --project ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/generator": "^7.0.0",
|
||||
"@babel/parser": "^7.1.0",
|
||||
"@babel/traverse": "^7.1.0",
|
||||
"codesandboxer-fs": "^0.3.1",
|
||||
"docz-utils": "^0.11.2",
|
||||
"hast-util-to-string": "^1.0.1",
|
||||
"jsx-ast-utils": "^2.0.1",
|
||||
"lodash.flatten": "^4.4.0",
|
||||
"lodash.get": "^4.4.2",
|
||||
"prettier": "^1.14.2",
|
||||
"signale": "^1.3.0",
|
||||
"strip-indent": "^2.0.0",
|
||||
"unist-util-is": "^2.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash.flatten": "^4.4.4",
|
||||
"@types/lodash.get": "^4.4.4"
|
||||
"@types/lodash.flatten": "^4.4.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import * as prettier from 'prettier'
|
||||
import logger from 'signale'
|
||||
|
||||
export const format = (code: string): Promise<string> =>
|
||||
new Promise((resolve, reject) => {
|
||||
try {
|
||||
const result = prettier.format(code, {
|
||||
parser: 'babylon',
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
})
|
||||
|
||||
resolve(result)
|
||||
} catch (err) {
|
||||
logger.fatal(err)
|
||||
resolve(err)
|
||||
}
|
||||
})
|
||||
@@ -1,60 +0,0 @@
|
||||
import * as parser from '@babel/parser'
|
||||
import traverse from '@babel/traverse'
|
||||
import * as generator from '@babel/generator'
|
||||
import get from 'lodash.get'
|
||||
|
||||
const fromSpecifiers = (specifiers: any = []) =>
|
||||
Array.isArray(specifiers) && specifiers.length > 0
|
||||
? specifiers.map(specifier => get(specifier, 'local.name'))
|
||||
: []
|
||||
|
||||
export const scopesFromEntry = (node: any) => {
|
||||
try {
|
||||
const ast = parser.parse(node.value, { sourceType: 'module' })
|
||||
const scopes: any[] = []
|
||||
|
||||
traverse(ast, {
|
||||
enter(path: any): void {
|
||||
if (path.isImportDeclaration()) {
|
||||
const sourceValue = get(path, 'node.source.value')
|
||||
|
||||
if (sourceValue !== 'docz') {
|
||||
scopes.push(...fromSpecifiers(path.node.specifiers))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
return scopes
|
||||
} catch (err) {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export const importsFromEntry = (node: any) => {
|
||||
try {
|
||||
const ast = parser.parse(node.value, { sourceType: 'module' })
|
||||
const imports: any[] = []
|
||||
|
||||
traverse(ast, {
|
||||
enter(path: any): void {
|
||||
if (path.isImportDeclaration()) {
|
||||
const sourceValue = get(path, 'node.source.value')
|
||||
|
||||
if (sourceValue !== 'docz') {
|
||||
const { code } = generator.default(path.node)
|
||||
imports.push(code)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
return imports
|
||||
} catch (err) {
|
||||
return []
|
||||
}
|
||||
}
|
||||
@@ -1,77 +1,54 @@
|
||||
import is from 'unist-util-is'
|
||||
import nodeToString from 'hast-util-to-string'
|
||||
import strip from 'strip-indent'
|
||||
import flatten from 'lodash.flatten'
|
||||
import * as path from 'path'
|
||||
|
||||
import { codeFromNode } from './code-from-node'
|
||||
import { importsFromEntry, scopesFromEntry } from './imports-from-entry'
|
||||
import { format } from './format'
|
||||
import { getCodeSandboxFiles } from './codesandbox-files'
|
||||
|
||||
const componentName = (value: any) => {
|
||||
const match = value.match(/^\<\\?(\w+)/)
|
||||
return match && match[1]
|
||||
}
|
||||
import is from 'unist-util-is'
|
||||
import flatten from 'lodash.flatten'
|
||||
import nodeToString from 'hast-util-to-string'
|
||||
import { jsx, imports as imps } from 'docz-utils'
|
||||
import { format } from 'docz-utils/lib/format'
|
||||
import { getSandboxImportInfo } from 'docz-utils/lib/codesandbox'
|
||||
|
||||
const isPlayground = (name: string) => name === 'Playground'
|
||||
|
||||
const isOpenTag = (p: any) =>
|
||||
p.isJSXOpeningElement() && isPlayground(p.node.name.name)
|
||||
|
||||
const isClosetag = (p: any) =>
|
||||
p.isJSXClosingElement() && isPlayground(p.node.name.name)
|
||||
|
||||
const removePlayground = (code: string) => {
|
||||
const open = codeFromNode(isOpenTag)
|
||||
const close = codeFromNode(isClosetag)
|
||||
|
||||
return code.replace(open(code), '').replace(close(code), '')
|
||||
}
|
||||
|
||||
const sanitizeCode = (code: string) =>
|
||||
strip(code)
|
||||
.trim()
|
||||
.replace(/'/g, `\\'`)
|
||||
.replace(/`/g, '\\`')
|
||||
|
||||
const addCodeProp = (
|
||||
const addPropsOnPlayground = async (
|
||||
node: any,
|
||||
idx: number,
|
||||
scopes: string[],
|
||||
imports: string[],
|
||||
cwd: string
|
||||
) => async (node: any, idx: number) => {
|
||||
const name = componentName(node.value)
|
||||
) => {
|
||||
const name = jsx.componentName(node.value)
|
||||
const tagOpen = new RegExp(`^\\<${name}`)
|
||||
|
||||
if (isPlayground(name)) {
|
||||
const formatted = await format(nodeToString(node))
|
||||
const code = formatted.slice(1, Infinity)
|
||||
const scope = `{props,${scopes.join(',')}}`
|
||||
const child = sanitizeCode(removePlayground(code))
|
||||
|
||||
let codeSandboxImportInfo: string | undefined
|
||||
try {
|
||||
const { parameters } = await getCodeSandboxFiles(child, imports, cwd)
|
||||
codeSandboxImportInfo = parameters
|
||||
} catch (e) {
|
||||
console.error('Could not create Open in CodeSandbox', e)
|
||||
}
|
||||
const child = jsx.sanitizeCode(jsx.removeTags(code))
|
||||
const codesandBoxInfo = await getSandboxImportInfo(child, imports, cwd)
|
||||
|
||||
node.value = node.value.replace(
|
||||
tagOpen,
|
||||
`<${name} __position={${idx}} __codesandbox={\`${codeSandboxImportInfo}\`} __code={\`${child}\`} __scope={${scope}}`
|
||||
`<${name} __position={${idx}} __codesandbox={\`${codesandBoxInfo}\`} __code={\`${child}\`} __scope={${scope}}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default () => (tree: any, fileInfo: any) => {
|
||||
const addComponentsProps = (
|
||||
scopes: string[],
|
||||
imports: string[],
|
||||
cwd: string
|
||||
) => async (node: any, idx: number) => {
|
||||
await addPropsOnPlayground(node, idx, scopes, imports, cwd)
|
||||
}
|
||||
|
||||
export default (root: string) => () => (tree: any, fileInfo: any) => {
|
||||
const importNodes = tree.children.filter((node: any) => is('import', node))
|
||||
const imports: string[] = flatten(importNodes.map(importsFromEntry))
|
||||
const scopes: string[] = flatten(importNodes.map(scopesFromEntry))
|
||||
const imports: string[] = flatten(importNodes.map(imps.getFullImports))
|
||||
const scopes: string[] = flatten(importNodes.map(imps.getImportsVariables))
|
||||
const fileCwd = path.relative(root, path.dirname(fileInfo.history[0]))
|
||||
|
||||
const nodes = tree.children
|
||||
.filter((node: any) => is('jsx', node))
|
||||
.map(addCodeProp(scopes, imports, path.dirname(fileInfo.history[0])))
|
||||
.map(addComponentsProps(scopes, imports, fileCwd))
|
||||
|
||||
return Promise.all(nodes).then(() => tree)
|
||||
}
|
||||
|
||||
8
packages/rehype-docz/src/types.d.ts
vendored
8
packages/rehype-docz/src/types.d.ts
vendored
@@ -1,8 +1,2 @@
|
||||
declare module 'unist-util-is'
|
||||
declare module 'hast-util-to-string'
|
||||
declare module 'strip-indent'
|
||||
declare module 'signale'
|
||||
declare module '@babel/parser'
|
||||
declare module '@babel/traverse'
|
||||
declare module '@babel/generator'
|
||||
declare module 'codesandboxer-fs'
|
||||
declare module 'unist-util-is'
|
||||
|
||||
Reference in New Issue
Block a user