Skip to content

Commit

Permalink
Merge pull request #195 from theKashey/improve-prefetching
Browse files Browse the repository at this point in the history
refine babel/webpack integration
  • Loading branch information
theKashey authored Jul 27, 2020
2 parents 4f6a78d + 15047a2 commit 0b55a54
Show file tree
Hide file tree
Showing 25 changed files with 429 additions and 292 deletions.
6 changes: 3 additions & 3 deletions .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ module.exports = [
{
path: ['dist/es2015/entrypoints/index.js', 'dist/es2015/entrypoints/boot.js'],
ignore: ['tslib'],
limit: '3.6 KB',
limit: '3.7 KB',
},
{
path: 'dist/es2015/entrypoints/index.js',
ignore: ['tslib'],
limit: '3.3 KB',
limit: '3.5 KB',
},
{
path: 'dist/es2015/entrypoints/boot.js',
ignore: ['tslib'],
limit: '1.8 KB',
limit: '1.9 KB',
},
];
6 changes: 3 additions & 3 deletions .size.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
{
"name": "dist/es2015/entrypoints/index.js, dist/es2015/entrypoints/boot.js",
"passed": true,
"size": 3601
"size": 3704
},
{
"name": "dist/es2015/entrypoints/index.js",
"passed": true,
"size": 3316
"size": 3414
},
{
"name": "dist/es2015/entrypoints/boot.js",
"passed": true,
"size": 1775
"size": 1863
}
]
2 changes: 1 addition & 1 deletion __tests__/__fixtures__/babel/webpack/actual.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import imported from 'react-imported-component';

const PreloadComponent = imported(() => import('./PreloadThis'));
const PrefetchChunkComponent = imported(() => import('./ChunkThis'));
const AsyncComponent0 = imported(() => import(/* webpackChunkName:namedChunk */'./MyComponent'));
const AsyncComponent0 = imported(() => import(/* webpackChunkName: "namedChunk" */'./MyComponent'));

const AsyncComponent1 = imported(() => import('./MyComponent'));

Expand Down
2 changes: 1 addition & 1 deletion __tests__/__fixtures__/babel/webpack/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const PrefetchChunkComponent = imported(() => importedWrapper("imported_fspdct_c
/* webpackPrefetch: true */
'./ChunkThis')));
const AsyncComponent0 = imported(() => importedWrapper("imported_18g2v0c_component", import(
/* webpackChunkName:namedChunk */
/* webpackChunkName: "namedChunk" */
'./MyComponent')));
const AsyncComponent1 = imported(() => importedWrapper("imported_18g2v0c_component", import('./MyComponent')));
const AsyncComponent2 = imported(async () => await importedWrapper("imported_18g2v0c_component", import('./MyComponent')));
Expand Down
12 changes: 5 additions & 7 deletions __tests__/rehydrate.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as React from 'react';
import * as ReactDOM from 'react-dom/server';
import { act } from 'react-dom/test-utils';

import { settings } from '../src/config';
import { setConfiguration } from '../src/configuration/config';
import { done as whenDone, toLoadable } from '../src/loadable/loadable';
import { createLoadableStream, drainHydrateMarks, rehydrateMarks } from '../src/loadable/marks';
import { ImportedComponent } from '../src/ui/Component';
Expand All @@ -22,10 +22,10 @@ describe('SSR Component', () => {

describe('client-rehydrate', () => {
beforeEach(() => {
settings.SSR = false;
setConfiguration({ SSR: false });
});
afterEach(() => {
settings.SSR = true;
setConfiguration({ SSR: true });
});

it('SSR green case', async () => {
Expand Down Expand Up @@ -110,11 +110,9 @@ describe('SSR Component', () => {

describe('server-rehydrate', () => {
beforeEach(() => {
settings.SSR = true;
});
afterEach(() => {
settings.SSR = true;
setConfiguration({ SSR: false });
});

it('green case', async () => {
const renderSpy2 = jest.fn().mockImplementation(A => <div>{A && <A />}</div>);
const Component = () => <div>loaded!</div>;
Expand Down
26 changes: 24 additions & 2 deletions __tests__/signatures.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,21 @@ describe('importMatch', () => {
getFunctionSignature('()=>$(`imported_-f5674t_component`,x.e(3).then(x.bind(null,`xxx`,7)))')
);
expect(getFunctionSignature('()=>$(`imported_-f5674t_component`,n.e(3).then(n.bind(null,`xxx`,7)))')).toEqual(
'()=>$(`imported_-f5674t_component`,-we(3).-wbind(null,`xxx`,7)))'
'()=>$(`imported_-f5674t_component`,-we().-wbind(null,`xxx`,7)))'
);
});

it('maps internal and external signatures', () => {
// internal is with Promise.resolve
// extenal is with webpack_require.e
expect(
getFunctionSignature(
'() => importedWrapper("imported_-1135avo_component", __webpack_require__.e(/*! import() */ 12).then(__webpack_require__.bind(null, /*! universal/components/SERP */ "./universal/components/SERP/index.js")))'
)
).toBe(
getFunctionSignature(
'() => importedWrapper("imported_-1135avo_component", Promise.resolve(/*! import() */).then(__webpack_require__.bind(null, /*! universal/components/SERP */ "./universal/components/SERP/index.js")))'
)
);
});

Expand All @@ -106,7 +120,15 @@ describe('importMatch', () => {
getFunctionSignature('()=>s("imported_-is59m_component",n.e(41).then(n.bind(null,"./Promo.jsx")))')
);
expect(getFunctionSignature('()=>P("imported_-is59m_component",t.e(41).then(t.bind(null,"./Promo.jsx")))')).toEqual(
'()=>$(`imported_-is59m_component`,-we(41).-wbind(null,`./Promo.jsx`)))'
'()=>$(`imported_-is59m_component`,-we().-wbind(null,`./Promo.jsx`)))'
);
});

it('fallback check: same signature, different function', () => {
expect(
getFunctionSignature('()=>P("imported_-one_component",t.e(41).then(t.bind(null,"./Promo.jsx")))')
).not.toEqual(
getFunctionSignature('()=>s("imported_-another_component",n.e(41).then(n.bind(null,"./Promo.jsx")))')
);
});
});
2 changes: 1 addition & 1 deletion __tests__/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ describe('scanForImports', () => {
(a, b) => a + b,
imports,
imported => imported.indexOf('c.js') < 0,
(imported, _, givenChunkName) => (imported.indexOf('a.js') > 0 ? `test-${givenChunkName}-test` : 'bundle-b')
(imported, _, options) => (imported.indexOf('a.js') > 0 ? `test-${options.chunkName}-test` : 'bundle-b')
);
expect(Object.values(imports)).toEqual([
`[() => import(/* webpackChunkName: \"chunk-a\" */'${rel}/a.js'), 'test-chunk-a-test', '${rel}/a.js', false] /* from .a */`,
Expand Down
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-imported-component",
"version": "6.3.5",
"version": "6.3.9",
"description": "I will import your component, and help to handle it",
"main": "dist/es5/entrypoints/index.js",
"jsnext:main": "dist/es2015/entrypoints/index.js",
Expand Down Expand Up @@ -62,7 +62,6 @@
"@babel/runtime": "^7.3.1",
"@size-limit/preset-small-lib": "^4.5.1",
"@theuiteam/lib-builder": "0.0.8",
"@types/detect-node": "^2.0.0",
"@types/enzyme": "^3.10.3",
"@types/node": "^12.12.6",
"@types/react-dom": "^16.9.4",
Expand All @@ -78,7 +77,7 @@
"dependencies": {
"babel-plugin-macros": "^2.6.1",
"crc-32": "^1.2.0",
"detect-node": "^2.0.4",
"detect-node-es": "^1.0.0",
"scan-directory": "^2.0.0",
"tslib": "^1.10.0"
},
Expand Down
177 changes: 177 additions & 0 deletions src/babel/babel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// @ts-ignore
import * as crc32 from 'crc-32';
import { existsSync } from 'fs';
import { dirname, join, relative, resolve } from 'path';

import { ImportedConfiguration } from '../configuration/configuration';
import { processComment } from './magic-comments';

export const encipherImport = (str: string) => {
return crc32.str(str).toString(32);
};

// Babel v7 compat
let syntax: any;
try {
syntax = require('babel-plugin-syntax-dynamic-import');
} catch (err) {
try {
syntax = require('@babel/plugin-syntax-dynamic-import');
} catch (e) {
throw new Error(
'react-imported-component babel plugin is requiring `babel-plugin-syntax-dynamic-import` or `@babel/plugin-syntax-dynamic-import` to work. Please add this dependency.'
);
}
}
syntax = syntax.default || syntax;

const resolveImport = (importName: string, file = '') => {
if (importName.charAt(0) === '.') {
return relative(process.cwd(), resolve(dirname(file), importName));
}
return importName;
};

const templateOptions = {
placeholderPattern: /^([A-Z0-9]+)([A-Z0-9_]+)$/,
};

function getImportArg(callPath: any) {
return callPath.get('arguments.0');
}

function getComments(callPath: any) {
return callPath.has('leadingComments') ? callPath.get('leadingComments') : [];
}

// load configuration
const configurationFile = join(process.cwd(), '.imported.js');
const defaultConfiguration: ImportedConfiguration = (existsSync(configurationFile)
? require(configurationFile)
: {}) as ImportedConfiguration;

export const createTransformer = (
{ types: t, template }: any,
excludeMacro = false,
configuration = defaultConfiguration
) => {
const headerTemplate = template(
`var importedWrapper = require('react-imported-component/wrapper');`,
templateOptions
);

const importRegistration = template('importedWrapper(MARK, IMPORT)', templateOptions);

const hasImports = new Set<string>();
const visitedNodes = new Map();

return {
traverse(programPath: any, fileName: string) {
let isBootstrapFile = false;

programPath.traverse({
ImportDeclaration(path: any) {
if (excludeMacro) {
return;
}

const source = path.node.source.value;
if (source === 'react-imported-component/macro') {
const { specifiers } = path.node;
path.remove();
const assignName = 'assignImportedComponents';
if (specifiers.length === 1 && specifiers[0].imported.name === assignName) {
isBootstrapFile = true;
programPath.node.body.unshift(
t.importDeclaration(
[t.importSpecifier(t.identifier(assignName), t.identifier(assignName))],
t.stringLiteral('react-imported-component/boot')
)
);
} else {
programPath.node.body.unshift(
t.importDeclaration(
specifiers.map((spec: any) =>
t.importSpecifier(t.identifier(spec.imported.name), t.identifier(spec.imported.name))
),
t.stringLiteral('react-imported-component')
)
);
}
}
},
Import({ parentPath }: any) {
if (visitedNodes.has(parentPath.node)) {
return;
}

const newImport = parentPath.node;
const rawImport = getImportArg(parentPath);
const importName = rawImport.node.value;
const rawComments = getComments(rawImport);
const comments = rawComments.map((parent: any) => parent.node.value);

const newComments = processComment(configuration, comments, importName, fileName, {
isBootstrapFile,
});

if (newComments !== comments) {
rawComments.forEach((comment: any) => comment.remove());
newComments.forEach((comment: string) => {
rawImport.addComment('leading', ` ${comment} `);
});
}

if (!importName) {
return;
}
const requiredFileHash = encipherImport(resolveImport(importName, fileName));

let replace = null;

replace = importRegistration({
MARK: t.stringLiteral(`imported_${requiredFileHash}_component`),
IMPORT: newImport,
});

hasImports.add(fileName);
visitedNodes.set(newImport, true);

parentPath.replaceWith(replace);
},
});
},

finish(node: any, filename: string) {
if (!hasImports.has(filename)) {
return;
}
node.body.unshift(headerTemplate());
},

hasImports,
};
};

export const babelPlugin = (babel: any, options: ImportedConfiguration = {}) => {
const transformer = createTransformer(babel, false, {
...defaultConfiguration,
...options,
});

return {
inherits: syntax,

visitor: {
Program: {
enter(programPath: any, { file }: any) {
transformer.traverse(programPath, file.opts.filename);
},

exit({ node }: any, { file }: any) {
transformer.finish(node, file.opts.filename);
},
},
},
};
};
Loading

0 comments on commit 0b55a54

Please sign in to comment.