1 1
import * as path from 'path'
2 1
import filter from 'lodash/filter'
3 1
import map from 'lodash/map'
4 1
import values from 'lodash/values'
5 1
import flatten from 'lodash/flatten'
6 1
import loaderUtils from 'loader-utils'
7 1
import { generate } from 'escodegen'
8 1
import toAst from 'to-ast'
9 1
import { builders as b } from 'ast-types'
10 1
import { compile } from 'vue-inbrowser-compiler'
11
import * as Rsg from 'react-styleguidist'
12 1
import chunkify from 'react-styleguidist/lib/loaders/utils/chunkify'
13 1
import getImports from 'react-styleguidist/lib/loaders/utils/getImports'
14 1
import requireIt from 'react-styleguidist/lib/loaders/utils/requireIt'
15 1
import resolveESModule from 'react-styleguidist/lib/loaders/utils/resolveESModule'
16
import { StyleguidistContext } from '../types/StyleGuide'
17 1
import expandDefaultComponent from './utils/expandDefaultComponent'
18 1
import getComponentVueDoc from './utils/getComponentVueDoc'
19 1
import importCodeExampleFile from './utils/importCodeExampleFile'
20 1
import absolutize from './utils/absolutize'
21 1
import getScript from './utils/getScript'
22 1
import getParser from './utils/getParser'
23

24 1
const REQUIRE_IN_RUNTIME_PATH = absolutize('requireInRuntime')
25 1
const EVAL_IN_CONTEXT_PATH = absolutize('evalInContext')
26 1
const JSX_COMPILER_UTILS_PATH = require.resolve('vue-inbrowser-compiler-utils')
27

28 1
function isVueFile(filepath: string) {
29 1
	return /.vue$/.test(filepath)
30
}
31

32 1
function isImport(req: any): req is { importPath: string; path: string } {
33 1
	return !!req.importPath
34
}
35

36 1
export default function (this: StyleguidistContext, source: string) {
37 1
	const callback = this.async()
38 1
	const cb = callback ? callback : () => null
39 1
	examplesLoader.call(this, source).then(res => cb(undefined, res))
40
}
41

42 1
export async function examplesLoader(this: StyleguidistContext, src: string): Promise<string> {
43 1
	const filePath = this.request.split('!').pop()
44 1
	let source: string | false = src
45 1
	if (!filePath) {
46 0
		return ''
47
	}
48 1
	if (isVueFile(filePath)) {
49
		// if it's a vue file, the examples could be in a docs block
50 0
		source = getComponentVueDoc(src, filePath)
51
	}
52 1
	const config = this._styleguidist
53 1
	const options = loaderUtils.getOptions(this) || {}
54 1
	const { file, displayName, shouldShowDefaultExample, customLangs } = options
55

56
	// Replace placeholders (__COMPONENT__) with the passed-in component name
57 1
	if (shouldShowDefaultExample && source) {
58 1
		const fullFilePath = path.join(path.dirname(filePath), file)
59 1
		const propsParser = getParser(config)
60
		try {
61 1
			const docs = await propsParser(fullFilePath)
62 1
			this.addDependency(fullFilePath)
63 1
			source = expandDefaultComponent(source, docs)
64
		} catch (e) {
65
			// eat the error since it will be reported by vuedoc-loader
66
		}
67
	}
68

69 1
	const updateExample = (example: Pick<Rsg.CodeExample, 'content' | 'lang' | 'settings'>) => {
70 1
		const p = importCodeExampleFile(example, this.resourcePath, this)
71 1
		if (p.settings) {
72 1
			const settings = p.settings
73
			// code to satisfy react-styleguidist and transfer properties to the props object
74
			// those properties will be added litterally to the wrapping div of the example
75 1
			const props = Object.keys(settings).reduce((obj: Record<string, any> | null, key: string) => {
76 1
				if (['style', 'className'].indexOf(key) !== -1 || /^data-/.test(key)) {
77 1
					obj = obj || {}
78 0
					obj[key] = settings[key]
79 0
					delete settings[key]
80
				}
81 1
				return obj
82
			}, null)
83 1
			if (props) {
84 0
				p.settings.props = props
85
			}
86
		}
87 1
		return config.updateExample ? config.updateExample(p, this.resourcePath) : p
88
	}
89

90
	// Load examples
91 1
	const examples = source ? chunkify(source, updateExample, customLangs) : []
92

93 1
	const getExampleLiveImports = (srci: string) => getImports(getScript(srci, config.jsxInExamples))
94

95
	// Find all import statements and require() calls in examples to make them
96
	// available in webpack context at runtime.
97
	// Note that we can't just use require() directly at runtime,
98
	// because webpack changes its name to something like __webpack__require__().
99 1
	const allCodeExamples = filter(examples, { type: 'code' })
100 1
	const requiresFromExamples = allCodeExamples.reduce(
101 1
		(requires: ({ importPath: string; path: string } | string)[], example) => {
102 1
			const requiresLocal = getExampleLiveImports(example.content)
103 1
			const importPath = example.settings && example.settings.importpath
104 1
			return requires.concat(
105 1
				importPath ? requiresLocal.map(p => ({ importPath, path: p })) : requiresLocal
106
			)
107
		},
108
		[]
109
	)
110

111
	// Auto imported modules.
112
	// We don't need to do anything here to support explicit imports: they will
113
	// work because both imports (generated below and by rewrite-imports) will
114
	// be eventually transpiled to `var x = require('x')`, so we'll just have two
115
	// of them in the same scope, which is fine in non-strict mode
116 1
	const fullContext = {
117
		// Modules, provied by the user
118
		...config.context,
119
		// Append the current component module to make it accessible in examples
120
		// without an explicit import
121 1
		...(displayName && config.jsxInExamples ? { [displayName]: file } : {})
122
	}
123

124
	// All required or imported modules
125 1
	const allModules = [...requiresFromExamples, ...values<string>(fullContext)]
126

127
	// “Prerequire” modules required in Markdown examples and context so they
128
	// end up in a bundle and be available at runtime
129 1
	const allModulesCode = allModules.reduce(
130 1
		(requires: Record<string, Record<string, any>>, requireRequest) => {
131
			// if we are looking at a remote example
132
			// resolve the requires from there
133

134 1
			if (isImport(requireRequest)) {
135 1
				if (!requires[requireRequest.importPath]) {
136 0
					requires[requireRequest.importPath] = {}
137
				}
138 0
				const relativePath = /^\./.test(requireRequest.path)
139 1
					? path.join(requireRequest.importPath, requireRequest.path)
140 0
					: requireRequest.path
141 0
				requires[requireRequest.importPath][requireRequest.path] = requireIt(relativePath)
142
			} else {
143 1
				requires[requireRequest] = requireIt(requireRequest)
144
			}
145 1
			return requires
146
		},
147
		{}
148
	)
149

150
	// Require context modules so they are available in an example
151

152 1
	const requireContextCode = b.program(flatten(map(fullContext, resolveESModule)))
153

154
	// Stringify examples object except the evalInContext function
155 1
	const examplesWithEval = examples.map(example => {
156 1
		if (example.type === 'code') {
157 1
			const importPath = example.settings && example.settings.importpath
158 1
			const evalInContext = {
159 1
				toAST: () =>
160 1
					b.callExpression(
161
						b.memberExpression(b.identifier('evalInContext'), b.identifier('bind')),
162
						[
163
							b.identifier('null'),
164
							b.callExpression(
165
								b.memberExpression(b.identifier('requireInRuntime'), b.identifier('bind')),
166 1
								[b.identifier('null'), importPath ? b.literal(importPath) : b.identifier('null')]
167
							)
168
						]
169
					)
170
			}
171

172 1
			if (config.codeSplit) {
173 0
				let compiled: any = false
174 1
				if (process.env.NODE_ENV === 'production') {
175
					// if we are not in prod, we want to avoid running examples through
176
					// buble all at the same time. We then tell it to calculate on the fly
177 0
					const compiledExample = compile(example.content, {
178
						...config.compilerConfig,
179 1
						...(config.jsxInExamples ? { jsx: '__pragma__(h)', objectAssign: 'concatenate' } : {})
180
					})
181 0
					compiled = {
182
						...compiledExample
183
					}
184
				}
185 0
				return { ...example, evalInContext, compiled }
186
			}
187

188 1
			return { ...example, evalInContext }
189
		}
190 1
		return example
191
	})
192

193 1
	return `
194
if (module.hot) {
195
	module.hot.accept([])
196
}
197
var requireMap = ${generate(toAst(allModulesCode))};
198
var requireInRuntimeBase = require(${JSON.stringify(REQUIRE_IN_RUNTIME_PATH)});
199
var requireInRuntime = requireInRuntimeBase.bind(null, requireMap);
200
var evalInContextBase = require(${JSON.stringify(EVAL_IN_CONTEXT_PATH)});${
201
		config.jsxInExamples
202 1
			? `
203

204
var compilerUtils = require(${JSON.stringify(JSX_COMPILER_UTILS_PATH)});
205
var evalInContext = evalInContextBase.bind(null, 
206
	${JSON.stringify(generate(requireContextCode))}, 
207
	compilerUtils.adaptCreateElement, compilerUtils.concatenate);`
208 1
			: `
209
var evalInContext = evalInContextBase.bind(null, 
210
	${JSON.stringify(generate(requireContextCode))}, 
211
	null, null)`
212
	}
213
module.exports = ${generate(toAst(examplesWithEval))}`
214
}

Read our documentation on viewing source code .

Loading