-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
examples-loader.js
89 lines (74 loc) · 3.2 KB
/
examples-loader.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
'use strict';
const path = require('path');
const filter = require('lodash/filter');
const map = require('lodash/map');
const reduce = require('lodash/reduce');
const loaderUtils = require('loader-utils');
const generate = require('escodegen').generate;
const toAst = require('to-ast');
const b = require('ast-types').builders;
const chunkify = require('./utils/chunkify');
const expandDefaultComponent = require('./utils/expandDefaultComponent');
const getRequires = require('./utils/getRequires');
const requireIt = require('./utils/requireIt');
const absolutize = filepath => path.resolve(__dirname, filepath);
function examplesLoader(source) {
const query = loaderUtils.getOptions(this) || {};
const config = this._styleguidist;
// Append React to context modules, since it’s required for JSX
const fullContext = Object.assign({ React: 'react' }, config.context);
// Replace placeholders (__COMPONENT__) with the passed-in component name
if (query.componentName) {
source = expandDefaultComponent(source, query.componentName);
}
const updateExample = config.updateExample
? props => config.updateExample(props, this.resourcePath)
: undefined;
// Load examples
const examples = chunkify(source, updateExample);
// We're analysing the examples' source code to figure out the require statements. We do it manually with regexes,
// because webpack unfortunately doesn't expose its smart logic for rewriting requires
// (https://webpack.github.io/docs/context.html). Note that we can't just use require(...) directly in runtime,
// because webpack changes its name to __webpack__require__ or something.
const codeFromAllExamples = map(filter(examples, { type: 'code' }), 'content').join('\n');
const requiresFromExamples = getRequires(codeFromAllExamples);
const allRequires = Object.assign({}, requiresFromExamples, fullContext);
// “Prerequire” modules required in Markdown examples and context so they end up in a bundle and be available at runtime
const allRequiresCode = reduce(
allRequires,
(requires, requireRequest) => {
requires[requireRequest] = requireIt(requireRequest);
return requires;
},
{}
);
// Require context modules so they are available in an example
const requireContextCode = b.program(
map(fullContext, (requireRequest, name) =>
b.variableDeclaration('var', [
b.variableDeclarator(b.identifier(name), requireIt(requireRequest).toAST()),
])
)
);
// Stringify examples object except the evalInContext function
const examplesWithEval = examples.map(example => {
if (example.type === 'code') {
example.evalInContext = { toAST: () => b.identifier('evalInContext') };
}
return example;
});
return `
if (module.hot) {
module.hot.accept([])
}
var requireMap = ${generate(toAst(allRequiresCode))};
var requireInRuntimeBase = require(${JSON.stringify(absolutize('utils/client/requireInRuntime'))});
var requireInRuntime = requireInRuntimeBase.bind(null, requireMap);
var evalInContextBase = require(${JSON.stringify(absolutize('utils/client/evalInContext'))});
var evalInContext = evalInContextBase.bind(null, ${JSON.stringify(
generate(requireContextCode)
)}, requireInRuntime);
module.exports = ${generate(toAst(examplesWithEval))}
`;
}
module.exports = examplesLoader;