fix(docz-core): data server rewriting files and refac utils (#370)

* chore: update dependencies

* chore: prettier fix

* fix(docz-core): data server rewriting files

* feat(rehype-docz): add __componentPath on PropsTable

* feat(docz-utils): add first version of package

* chore(docz-utils): refac to include some more utils

* chore(docz-core): remove unnescessary code

* chore: tslint error
This commit is contained in:
Pedro Nauck
2018-09-27 20:30:17 -03:00
committed by GitHub
parent 27d04c0268
commit b157060e45
41 changed files with 4026 additions and 1239 deletions

View File

@@ -7,14 +7,14 @@
"build": "docz build"
},
"dependencies": {
"emotion": "^9.2.8",
"emotion": "^9.2.9",
"prop-types": "^15.6.2",
"react": "^16.5.0",
"react-dom": "^16.5.0",
"react-emotion": "^9.2.8"
"react": "^16.5.2",
"react-dom": "^16.5.2",
"react-emotion": "^9.2.9"
},
"devDependencies": {
"babel-plugin-emotion": "^9.2.8",
"babel-plugin-emotion": "^9.2.9",
"docz": "^0.11.2"
}
}

View File

@@ -7,17 +7,17 @@
"build": "docz build"
},
"dependencies": {
"emotion": "^9.2.8",
"emotion": "^9.2.9",
"prop-types": "^15.6.2",
"react": "^16.5.0",
"react-dom": "^16.5.0",
"react-emotion": "^9.2.8"
"react": "^16.5.2",
"react-dom": "^16.5.2",
"react-emotion": "^9.2.9"
},
"devDependencies": {
"@babel/preset-flow": "^7.0.0",
"babel-plugin-emotion": "^9.2.8",
"babel-plugin-emotion": "^9.2.9",
"docz": "^0.11.2",
"flow-bin": "^0.80.0",
"flow-bin": "^0.81.0",
"flow-typed": "^2.5.1"
}
}

View File

@@ -8,9 +8,9 @@
},
"dependencies": {
"prop-types": "^15.6.2",
"react": "^16.5.0",
"react-dom": "^16.5.0",
"styled-components": "^3.4.5"
"react": "^16.5.2",
"react-dom": "^16.5.2",
"styled-components": "^3.4.9"
},
"devDependencies": {
"docz": "^0.11.2"

View File

@@ -9,12 +9,12 @@
"dependencies": {
"docz": "^0.11.2",
"docz-core": "^0.11.2",
"emotion": "^9.2.8",
"react": "^16.5.0",
"react-dom": "^16.5.0",
"react-emotion": "^9.2.8"
"emotion": "^9.2.9",
"react": "^16.5.2",
"react-dom": "^16.5.2",
"react-emotion": "^9.2.9"
},
"devDependencies": {
"babel-plugin-emotion": "^9.2.8"
"babel-plugin-emotion": "^9.2.9"
}
}

View File

@@ -37,15 +37,15 @@
"commitizen": "^2.10.1",
"del": "^3.0.0",
"husky": "^1.0.0-rc.13",
"lerna": "3.3.0",
"libundler": "^1.7.1",
"lerna": "3.4.0",
"libundler": "^1.7.6",
"lint-staged": "^7.2.2",
"npm-run-all": "^4.1.3",
"prettier": "^1.14.2",
"trash-cli": "^1.4.0",
"tslint": "^5.11.0",
"tslint-config-prettier": "^1.15.0",
"typescript": "^2.9.2"
"typescript": "^3.0.3"
},
"workspaces": [
"packages/*",

View File

@@ -22,17 +22,17 @@
"tslint": "tslint --project ."
},
"dependencies": {
"@babel/core": "7.0.0",
"@babel/plugin-proposal-class-properties": "7.0.0",
"@babel/core": "7.1.0",
"@babel/plugin-proposal-class-properties": "7.1.0",
"@babel/plugin-proposal-object-rest-spread": "7.0.0",
"@babel/plugin-syntax-dynamic-import": "7.0.0",
"@babel/plugin-transform-destructuring": "7.0.0",
"@babel/plugin-transform-regenerator": "7.0.0",
"@babel/plugin-transform-runtime": "7.0.0",
"@babel/preset-env": "7.0.0",
"@babel/plugin-transform-runtime": "7.1.0",
"@babel/preset-env": "7.1.0",
"@babel/preset-flow": "7.0.0",
"@babel/preset-react": "7.0.0",
"@babel/preset-typescript": "7.0.0",
"@babel/preset-typescript": "7.1.0",
"babel-plugin-macros": "^2.4.0",
"babel-plugin-react-docgen": "^2.0.0",
"babel-plugin-transform-dynamic-import": "^2.0.0",

View File

@@ -22,7 +22,7 @@
"tslint": "tslint --project ."
},
"dependencies": {
"@babel/core": "7.0.0",
"@babel/core": "7.1.0",
"@babel/polyfill": "7.0.0",
"@babel/runtime": "^7.0.0",
"@mdx-js/loader": "^0.15.2",
@@ -41,6 +41,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",
@@ -53,7 +54,7 @@
"happypack": "^5.0.0",
"html-minifier": "^3.5.20",
"humanize-string": "^1.0.2",
"koa": "^2.5.2",
"koa": "^2.5.3",
"koa-connect": "^2.0.1",
"koa-mount": "^3.0.0",
"koa-range": "^0.3.0",
@@ -61,37 +62,29 @@
"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.6",
"react-hot-loader": "4.3.8",
"rehype-docz": "^0.11.0",
"rehype-slug": "^2.0.1",
"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.2.1",
"signale": "^1.3.0",
"titleize": "^1.0.1",
"to-vfile": "^5.0.1",
"uglifyjs-webpack-plugin": "^1.3.0",
"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.17.2",
"webpack-chain": "^4.10.0",
"webpack": "^4.19.1",
"webpack-chain": "^4.11.0",
"webpack-hot-client": "^4.1.1",
"webpack-manifest-plugin": "^2.0.3",
"webpack-manifest-plugin": "^2.0.4",
"webpack-serve": "^2.0.2",
"webpack-serve-overlay": "^0.3.0",
"webpack-serve-waitpage": "^1.0.2",
"webpackbar": "^2.6.3",
"ws": "^6.0.0",
"yargs": "^12.0.1"
"yargs": "^12.0.2"
},
"devDependencies": {
"@types/chokidar": "^1.7.5",
@@ -102,13 +95,13 @@
"@types/html-webpack-plugin": "^3.2.0",
"@types/koa": "^2.0.46",
"@types/lodash.get": "^4.4.4",
"@types/node": "10.9.4",
"@types/node": "10.10.1",
"@types/prettier": "^1.13.2",
"@types/resolve": "^0.0.8",
"@types/webpack": "^4.4.11",
"@types/webpack-chain": "^4.8.1",
"@types/ws": "^6.0.1",
"@types/yargs": "^11.1.1",
"@types/yargs": "^12.0.0",
"tslint": "^5.11.0",
"tslint-config-prettier": "^1.15.0",
"typescript": "^3.0.1"

View File

@@ -60,7 +60,7 @@ export class DataServer {
)
)
await touch(paths.db, JSON.stringify(this.state, null, 2))
this.updateStateFile()
}
public async listen(): Promise<void> {
@@ -97,6 +97,11 @@ export class DataServer {
return (key: string, val: any): void => {
this.state[key] = val
send(`state.${key}`, val)
this.updateStateFile()
}
}
private async updateStateFile(): Promise<void> {
await touch(paths.db, JSON.stringify(this.state, null, 2))
}
}

View File

@@ -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,

View File

@@ -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
}

View File

@@ -6,7 +6,7 @@ import { minify } from 'html-minifier'
import miniHtmlWebpack from 'mini-html-webpack-plugin'
import friendlyErrors from 'friendly-errors-webpack-plugin'
import manifestPlugin from 'webpack-manifest-plugin'
import UglifyJs from 'uglifyjs-webpack-plugin'
import TerserPlugin from 'terser-webpack-plugin'
import * as loaders from './loaders'
import * as paths from '../../config/paths'
@@ -15,11 +15,8 @@ import { Config as Args, Env } from '../../commands/args'
import { BabelRC } from '../../utils/babel-config'
import { parseHtml, htmlTemplate } from '../../utils/parse-html'
const uglify = new UglifyJs({
parallel: true,
cache: true,
sourceMap: true,
uglifyOptions: {
const terser = new TerserPlugin({
terserOptions: {
parse: {
ecma: 8,
},
@@ -37,6 +34,9 @@ const uglify = new UglifyJs({
ascii_only: true,
},
},
parallel: true,
cache: true,
sourceMap: true,
})
export const createConfig = (args: Args, env: Env) => async (
@@ -105,7 +105,7 @@ export const createConfig = (args: Args, env: Env) => async (
},
...(isProd && {
minimize: true,
minimizer: [uglify],
minimizer: [terser],
}),
},
})
@@ -123,9 +123,9 @@ export const createConfig = (args: Args, env: Env) => async (
* resolve
*/
config.resolve
.set('symlinks', true)
.extensions.add('.web.js')
config.resolve.set('symlinks', true)
config.resolve.extensions
.add('.web.js')
.add('.mjs')
.add('.js')
.add('.json')

View File

@@ -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()

View File

@@ -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()

View File

@@ -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]

View File

@@ -1,7 +1,6 @@
import * as fs from 'fs-extra'
import { load, finds } from 'load-cfg'
import chokidar from 'chokidar'
import equal from 'fast-deep-equal'
import get from 'lodash.get'
import { Params, State } from '../DataServer'
@@ -36,18 +35,13 @@ const getInitialConfig = (config: Config): Payload => {
}
}
const updateConfig = (config: Config) => async (p: Params) => {
const old = p.state.config
const newConfig = load('docz', getInitialConfig(config), true, false)
if (newConfig && !equal(old, newConfig)) {
p.setState('config', newConfig)
}
}
const updateConfig = (config: Config) => async ({ setState }: Params) =>
setState('config', load('docz', getInitialConfig(config), true, false))
export const state = (config: Config): State => {
const watcher = chokidar.watch(finds('docz'), {
cwd: paths.root,
ignored: /(^|[\/\\])\../,
persistent: true,
})
@@ -55,10 +49,11 @@ export const state = (config: Config): State => {
init: updateConfig(config),
update: async params => {
const update = updateConfig(config)
const fn = async () => update(params)
watcher.on('add', async () => update(params))
watcher.on('change', async () => update(params))
watcher.on('unlink', async () => update(params))
watcher.on('add', fn)
watcher.on('change', fn)
watcher.on('unlink', fn)
return () => watcher.close()
},

View File

@@ -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, {

View File

@@ -9,29 +9,21 @@ 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'
declare module 'uglifyjs-webpack-plugin'
declare module 'terser-webpack-plugin'
declare module 'lodash.get'
declare module 'signale'
declare module 'webpackbar'

View File

@@ -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) => {

View File

@@ -29,12 +29,9 @@ export const loadConfig = (args: Config): Config => {
modifyBabelRc: (babelrc: BabelRC) => babelrc,
}
let config
if (args.config) {
config = loadFrom<Config>(path.resolve(args.config), defaultConfig)
} else {
config = load<Config>('docz', defaultConfig)
}
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 }))

View File

@@ -24,8 +24,8 @@
"codemirror": "^5.40.0",
"copy-text-to-clipboard": "^1.0.4",
"docz": "^0.11.2",
"emotion": "^9.2.8",
"emotion-theming": "^9.2.6",
"emotion": "^9.2.9",
"emotion-theming": "^9.2.9",
"facepaint": "^1.2.1",
"hotkeys-js": "^3.3.7",
"lodash.flattendepth": "^4.7.0",
@@ -35,15 +35,15 @@
"polished": "^2.0.3",
"pretty": "^2.0.0",
"prop-types": "15.6.2",
"re-resizable": "^4.8.1",
"react": "^16.5.0",
"re-resizable": "^4.9.0",
"react": "^16.5.2",
"react-codemirror2": "^5.1.0",
"react-dom": "^16.5.0",
"react-emotion": "^9.2.8",
"react-dom": "^16.5.2",
"react-emotion": "^9.2.9",
"react-feather": "^1.1.3",
"react-lightweight-tooltip": "^1.0.0",
"react-live": "^1.11.0",
"react-perfect-scrollbar": "^1.2.0",
"react-perfect-scrollbar": "^1.2.1",
"react-powerplug": "^1.0.0-rc.1",
"react-sizes": "^1.0.4",
"webfontloader": "^1.6.28"
@@ -53,21 +53,21 @@
"react-dom": "^16.2.0"
},
"devDependencies": {
"@babel/core": "7.0.0",
"@babel/core": "7.1.0",
"@types/lodash.flattendepth": "^4.7.4",
"@types/lodash.get": "^4.4.4",
"@types/react": "^16.4.7",
"@types/react": "^16.4.14",
"@types/react-dom": "^16.0.6",
"babel-loader": "^8.0.2",
"babel-plugin-emotion": "^9.2.8",
"babel-plugin-emotion": "^9.2.9",
"css-loader": "^1.0.0",
"cross-env": "^5.2.0",
"filemanager-webpack-plugin": "^2.0.4",
"filemanager-webpack-plugin": "^2.0.5",
"style-loader": "^0.23.0",
"ts-loader": "^4.5.0",
"uglifyjs-webpack-plugin": "^1.3.0",
"webpack": "^4.17.2",
"webpack-bundle-analyzer": "^2.13.1",
"terser-webpack-plugin": "^1.1.0",
"ts-loader": "^5.1.1",
"webpack": "^4.19.1",
"webpack-bundle-analyzer": "^3.0.2",
"webpack-cli": "^3.1.0"
}
}

View File

@@ -2,6 +2,9 @@ import * as React from 'react'
export const CodeSandboxLogo = (props: any) => (
<svg {...props} viewBox="0 0 512 512">
<path d="M69.2898098,165.083974 L69.2898098,276.649443 L152.161311,324.692718 L152.161311,412.603224 L241.327633,463.829131 L241.327633,264.06328 L69.2898098,165.083974 Z M89.0172642,137.098529 L260.121958,235.540974 L427.640018,138.456525 L339.210941,87.2017661 L262.258901,131.853758 L179.736828,84.2839889 L89.0172642,137.098529 Z M272.206216,463.739666 L370.845646,406.905256 L370.845646,322.809124 L444.244039,280.276172 L444.244039,167.397587 L272.206216,266.116045 L272.206216,463.739666 Z M255.633239,512 L34,384.729507 L34,128.977638 L255.644267,0 L477.328236,128.432852 L477.328236,384.321468 L255.633239,512 Z" fill="currentColor" />
<path
d="M69.2898098,165.083974 L69.2898098,276.649443 L152.161311,324.692718 L152.161311,412.603224 L241.327633,463.829131 L241.327633,264.06328 L69.2898098,165.083974 Z M89.0172642,137.098529 L260.121958,235.540974 L427.640018,138.456525 L339.210941,87.2017661 L262.258901,131.853758 L179.736828,84.2839889 L89.0172642,137.098529 Z M272.206216,463.739666 L370.845646,406.905256 L370.845646,322.809124 L444.244039,280.276172 L444.244039,167.397587 L272.206216,266.116045 L272.206216,463.739666 Z M255.633239,512 L34,384.729507 L34,128.977638 L255.644267,0 L477.328236,128.432852 L477.328236,384.321468 L255.633239,512 Z"
fill="currentColor"
/>
</svg>
)

View File

@@ -1,6 +1,6 @@
const path = require('path')
const webpack = require('webpack')
const UglifyJs = require('uglifyjs-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
const FileManagerPlugin = require('filemanager-webpack-plugin')
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
@@ -51,11 +51,8 @@ const externals = deps
.concat(externalList)
.filter(dep => internals.indexOf(dep) === -1)
const uglify = new UglifyJs({
parallel: true,
cache: true,
sourceMap: true,
uglifyOptions: {
const minify = new TerserPlugin({
terserOptions: {
parse: {
ecma: 8,
},
@@ -73,6 +70,9 @@ const uglify = new UglifyJs({
ascii_only: true,
},
},
parallel: true,
cache: true,
sourceMap: true,
})
const plugins = [
@@ -132,7 +132,7 @@ module.exports = {
namedModules: true,
...(IS_PROD && {
minimize: true,
minimizer: [uglify],
minimizer: [minify],
}),
},
performance: {

View 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"
}
}

View File

@@ -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)

View File

@@ -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
}

View 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)
)

View 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 }

View 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]
}

View File

@@ -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
View 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'

View 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/**"]
}

View File

@@ -0,0 +1,3 @@
{
"extends": "../../tslint.json"
}

View File

@@ -34,17 +34,17 @@
"deepmerge": "^2.1.1",
"docz-core": "^0.11.2",
"docz-theme-default": "^0.11.2",
"lodash": "^4.17.10",
"lodash": "^4.17.11",
"prop-types": "^15.6.2",
"react": "^16.5.0",
"react": "^16.5.2",
"react-copy-write": "^0.8.0",
"react-dom": "^16.5.0",
"react-dom": "^16.5.2",
"react-imported-component": "^4.6.2",
"react-router": "^4.3.1",
"react-router-dom": "^4.3.1",
"react-router-hash-link": "^1.2.0",
"ulid": "^2.3.0",
"yargs": "^12.0.1"
"yargs": "^12.0.2"
},
"peerDependencies": {
"react": "^16.2.0",
@@ -53,9 +53,9 @@
"devDependencies": {
"@types/deepmerge": "^2.1.0",
"@types/lodash": "^4.14.116",
"@types/react": "^16.4.7",
"@types/react": "^16.4.14",
"@types/react-copy-write": "^0.7.1",
"@types/react-dom": "^16.0.6",
"@types/react-router-dom": "^4.3.0"
"@types/react-router-dom": "^4.3.1"
}
}

View File

@@ -23,12 +23,12 @@
},
"dependencies": {
"deepmerge": "^2.1.1",
"esm": "^3.0.82",
"esm": "^3.0.84",
"find-up": "^3.0.0",
"fs-extra": "^7.0.0"
},
"devDependencies": {
"@types/find-up": "^2.1.1",
"@types/node": "^10.9.4"
"@types/node": "^10.10.1"
}
}

View File

@@ -22,20 +22,13 @@
"tslint": "tslint --project ."
},
"dependencies": {
"@babel/generator": "^7.0.0",
"@babel/parser": "^7.0.0",
"@babel/traverse": "^7.0.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.2.1",
"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"
}
}

View File

@@ -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)
}
})

View File

@@ -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 []
}
}

View File

@@ -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)
}

View File

@@ -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'

4565
yarn.lock

File diff suppressed because it is too large Load Diff