Skip to content

Commit ebb086f

Browse files
committed
feat(bootstrap): support bootstrap scripts for directives
A directive can provide a bootstrap export that is called a single time when the application starts. in order to allow directives to setup global singletons, shared stores etc.
1 parent 46263b8 commit ebb086f

File tree

7 files changed

+101
-5
lines changed

7 files changed

+101
-5
lines changed

src/babelPluginTransformJsxDirectives.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,20 @@ export default function babelPluginTransformJsxDirectives(babel) {
6464
name: directiveName,
6565
as,
6666
source,
67+
bootstrap,
6768
options,
6869
globalOptions,
6970
},
7071
i
7172
) => {
72-
const localName = importDirective(babel, path, directiveName, source);
73+
const localName = importDirective(
74+
babel,
75+
path,
76+
directiveName,
77+
source,
78+
bootstrap,
79+
globalOptions
80+
);
7381

7482
const isOuter = i === directives.length - 1;
7583
if (!isOuter) {

src/getApplicableDirectives.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export default function getApplicableDirectives(babel, path, directives) {
3232
name: directiveName,
3333
type,
3434
source,
35+
bootstrap,
3536
transformOptions,
3637
globalOptions,
3738
}
@@ -50,6 +51,7 @@ export default function getApplicableDirectives(babel, path, directives) {
5051
const directive = {
5152
name: directiveName,
5253
globalOptions,
54+
bootstrap,
5355
type,
5456
source,
5557
};

src/getImport.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import getRootPath from './getRootPath';
22

3-
export default function getImport({ types: t }, path, directiveName, directiveSource) {
3+
export default function getImport({ types: t }, path, directiveName, directiveSource, bootstrap) {
44
const rootPath = getRootPath(path);
55

66
const existingImport = rootPath.get('body').find((bodyPath) => {
@@ -16,9 +16,19 @@ export default function getImport({ types: t }, path, directiveName, directiveSo
1616
}
1717

1818
const uid = rootPath.scope.generateUidIdentifier(`${directiveName}Directive`);
19+
const bootstrapUid = rootPath.scope.generateUidIdentifier(`${directiveName}Bootstrap`);
20+
21+
const specifiers = [t.importDefaultSpecifier(uid)];
22+
23+
if (bootstrap != null) {
24+
specifiers.push(t.importSpecifier(
25+
bootstrapUid,
26+
t.identifier('bootstrap')
27+
));
28+
}
1929

2030
const newImport = t.importDeclaration(
21-
[t.importDefaultSpecifier(uid)],
31+
specifiers,
2232
t.stringLiteral(directiveSource)
2333
);
2434

src/importDirective.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
11
import getImport from './getImport';
2+
import insertBootstrap from './insertBootstrap';
23

3-
export default function importDirective(babel, path, directiveName, directiveSource) {
4-
const someImport = getImport(babel, path, directiveName, directiveSource);
4+
export default function importDirective(
5+
babel,
6+
path,
7+
directiveName,
8+
directiveSource,
9+
bootstrap
10+
) {
11+
const someImport = getImport(babel, path, directiveName, directiveSource, bootstrap);
12+
13+
if (bootstrap != null) {
14+
insertBootstrap(babel, someImport, bootstrap);
15+
}
516

617
return someImport.get('specifiers.0.local.name').node;
718
}

src/insertBootstrap.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import template from 'babel-template';
2+
3+
const bootstrapTemplate = `
4+
if (!BOOTSTRAP_NAME.__called) {
5+
BOOTSTRAP_NAME.__called = true;
6+
BOOTSTRAP_NAME(BOOTSTRAP_OPTIONS);
7+
}
8+
`;
9+
10+
function appendBootstrap(t, programBody, bootstrapNode) {
11+
programBody.find((childPath) => {
12+
return !t.isImportDeclaration(childPath);
13+
}).insertBefore(bootstrapNode);
14+
}
15+
16+
export default function insertBootstrap({ types: t }, importPath, options) {
17+
const BOOTSTRAP_NAME = t.identifier(importPath.get('specifiers.1.local.name').node);
18+
19+
const buildGlobalOptionsNode = template(`var x = ${JSON.stringify(options)};`);
20+
const BOOTSTRAP_OPTIONS = buildGlobalOptionsNode().declarations[0].init;
21+
22+
const buildBootstrap = template(bootstrapTemplate);
23+
24+
const bootstrapNode = buildBootstrap({
25+
BOOTSTRAP_NAME,
26+
BOOTSTRAP_OPTIONS,
27+
});
28+
29+
appendBootstrap(
30+
t,
31+
importPath.parentPath.get('body'),
32+
bootstrapNode
33+
);
34+
}

test/__snapshots__/spec.js.snap

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,20 @@ exports[`babel-plugin-transform-jsx-directives applies transformOptions 1`] = `
3030
}} next={(_Elm, _props) => <_Elm {..._props} />} />;"
3131
`;
3232

33+
exports[`babel-plugin-transform-jsx-directives bootstrap bootstrap directives 1`] = `
34+
"import _bootstrapDirective, { bootstrap as _bootstrapBootstrap } from \\"bootstrap.js\\";
35+
36+
if (!_bootstrapBootstrap.__called) {
37+
_bootstrapBootstrap.__called = true;
38+
39+
_bootstrapBootstrap({
40+
\\"foo\\": \\"bar\\"
41+
});
42+
}
43+
44+
<_bootstrapDirective Elm=\\"div\\" props={{}} options=\\"baz\\" next={(_Elm, _props) => <_Elm {..._props} />} />;"
45+
`;
46+
3347
exports[`babel-plugin-transform-jsx-directives converts jsx spread operators to object spread 1`] = `
3448
"import _htmlDirective from \\"./test/directives/html.js\\";
3549

test/spec.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,4 +284,21 @@ describe('babel-plugin-transform-jsx-directives', () => {
284284
expect(code).toMatchSnapshot();
285285
});
286286
});
287+
288+
describe('bootstrap', () => {
289+
it('bootstrap directives', () => {
290+
const code = transform(
291+
`
292+
<div bootstrap="baz" />
293+
`,
294+
{
295+
directives: [
296+
[{ name: 'bootstrap', source: 'bootstrap.js', bootstrap: { foo: 'bar' } }],
297+
],
298+
}
299+
);
300+
301+
expect(code).toMatchSnapshot();
302+
});
303+
});
287304
});

0 commit comments

Comments
 (0)