Skip to content

Commit

Permalink
feat: Allow additional identifiers to be passed in to the babel plugin (
Browse files Browse the repository at this point in the history
#966)

* Allow additional identifiers to be passed in to the babel plugin

* Now allowing an array of name/package entries to be transformed
  • Loading branch information
edkimmel committed Jul 6, 2023
1 parent 2d1645d commit e18e37a
Show file tree
Hide file tree
Showing 3 changed files with 364 additions and 23 deletions.
300 changes: 295 additions & 5 deletions packages/babel-plugin/src/__snapshots__/index.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,244 @@ loadable({
});"
`;
exports[`plugin custom signatures named signature should not match default import 1`] = `
"import myLoadable from 'myLoadablePackage';
myLoadable(() => import(\`./ModA\`));"
`;
exports[`plugin custom signatures should match custom default signature 1`] = `
"import myLoadable from 'myLoadablePackage';
myLoadable({
resolved: {},
chunkName() {
return \`ModA\`.replace(/[^a-zA-Z0-9_$()=\\\\-^°]+/g, \\"-\\");
},
isReady(props) {
const key = this.resolve(props);
if (this.resolved[key] !== true) {
return false;
}
if (typeof __webpack_modules__ !== 'undefined') {
return !!__webpack_modules__[key];
}
return false;
},
importAsync: () => import(
/* webpackChunkName: \\"ModA\\" */
\`./ModA\`),
requireAsync(props) {
const key = this.resolve(props);
this.resolved[key] = false;
return this.importAsync(props).then(resolved => {
this.resolved[key] = true;
return resolved;
});
},
requireSync(props) {
const id = this.resolve(props);
if (typeof __webpack_require__ !== 'undefined') {
return __webpack_require__(id);
}
return eval('module.require')(id);
},
resolve() {
if (require.resolveWeak) {
return require.resolveWeak(\`./ModA\`);
}
return eval('require.resolve')(\`./ModA\`);
}
});"
`;
exports[`plugin custom signatures should match custom named signature 1`] = `
"import { myLoadable } from 'myLoadablePackage';
myLoadable({
resolved: {},
chunkName() {
return \`ModA\`.replace(/[^a-zA-Z0-9_$()=\\\\-^°]+/g, \\"-\\");
},
isReady(props) {
const key = this.resolve(props);
if (this.resolved[key] !== true) {
return false;
}
if (typeof __webpack_modules__ !== 'undefined') {
return !!__webpack_modules__[key];
}
return false;
},
importAsync: () => import(
/* webpackChunkName: \\"ModA\\" */
\`./ModA\`),
requireAsync(props) {
const key = this.resolve(props);
this.resolved[key] = false;
return this.importAsync(props).then(resolved => {
this.resolved[key] = true;
return resolved;
});
},
requireSync(props) {
const id = this.resolve(props);
if (typeof __webpack_require__ !== 'undefined') {
return __webpack_require__(id);
}
return eval('module.require')(id);
},
resolve() {
if (require.resolveWeak) {
return require.resolveWeak(\`./ModA\`);
}
return eval('require.resolve')(\`./ModA\`);
}
});"
`;
exports[`plugin custom signatures should match renamed default import 1`] = `
"import renamedLoadable from '@loadable/component';
renamedLoadable({
resolved: {},
chunkName() {
return \`ModA\`.replace(/[^a-zA-Z0-9_$()=\\\\-^°]+/g, \\"-\\");
},
isReady(props) {
const key = this.resolve(props);
if (this.resolved[key] !== true) {
return false;
}
if (typeof __webpack_modules__ !== 'undefined') {
return !!__webpack_modules__[key];
}
return false;
},
importAsync: () => import(
/* webpackChunkName: \\"ModA\\" */
\`./ModA\`),
requireAsync(props) {
const key = this.resolve(props);
this.resolved[key] = false;
return this.importAsync(props).then(resolved => {
this.resolved[key] = true;
return resolved;
});
},
requireSync(props) {
const id = this.resolve(props);
if (typeof __webpack_require__ !== 'undefined') {
return __webpack_require__(id);
}
return eval('module.require')(id);
},
resolve() {
if (require.resolveWeak) {
return require.resolveWeak(\`./ModA\`);
}
return eval('require.resolve')(\`./ModA\`);
}
});"
`;
exports[`plugin custom signatures should match simple default import 1`] = `
"import loadable from '@loadable/component';
loadable({
resolved: {},
chunkName() {
return \`ModA\`.replace(/[^a-zA-Z0-9_$()=\\\\-^°]+/g, \\"-\\");
},
isReady(props) {
const key = this.resolve(props);
if (this.resolved[key] !== true) {
return false;
}
if (typeof __webpack_modules__ !== 'undefined') {
return !!__webpack_modules__[key];
}
return false;
},
importAsync: () => import(
/* webpackChunkName: \\"ModA\\" */
\`./ModA\`),
requireAsync(props) {
const key = this.resolve(props);
this.resolved[key] = false;
return this.importAsync(props).then(resolved => {
this.resolved[key] = true;
return resolved;
});
},
requireSync(props) {
const id = this.resolve(props);
if (typeof __webpack_require__ !== 'undefined') {
return __webpack_require__(id);
}
return eval('module.require')(id);
},
resolve() {
if (require.resolveWeak) {
return require.resolveWeak(\`./ModA\`);
}
return eval('require.resolve')(\`./ModA\`);
}
});"
`;
exports[`plugin custom signatures should not match on undeclared specifiers 1`] = `
"import myLoadable from 'myLoadablePackage';
myLoadable(() => import(\`./ModA\`));"
`;
exports[`plugin loadable.lib should be transpiled too 1`] = `
"import loadable from '@loadable/component';
loadable.lib({
Expand Down Expand Up @@ -700,11 +938,6 @@ loadable({
});"
`;
exports[`plugin simple import should not work with renamed specifier by default 1`] = `
"import renamedLoadable from '@loadable/component';
renamedLoadable(() => import(\`./ModA\`));"
`;
exports[`plugin simple import should transform path into "chunk-friendly" name 1`] = `
"import loadable from '@loadable/component';
loadable({
Expand Down Expand Up @@ -990,6 +1223,63 @@ renamedLazy({
});"
`;
exports[`plugin simple import should work with renamed specifier by default 1`] = `
"import renamedLoadable from '@loadable/component';
renamedLoadable({
resolved: {},
chunkName() {
return \`ModA\`.replace(/[^a-zA-Z0-9_$()=\\\\-^°]+/g, \\"-\\");
},
isReady(props) {
const key = this.resolve(props);
if (this.resolved[key] !== true) {
return false;
}
if (typeof __webpack_modules__ !== 'undefined') {
return !!__webpack_modules__[key];
}
return false;
},
importAsync: () => import(
/* webpackChunkName: \\"ModA\\" */
\`./ModA\`),
requireAsync(props) {
const key = this.resolve(props);
this.resolved[key] = false;
return this.importAsync(props).then(resolved => {
this.resolved[key] = true;
return resolved;
});
},
requireSync(props) {
const id = this.resolve(props);
if (typeof __webpack_require__ !== 'undefined') {
return __webpack_require__(id);
}
return eval('module.require')(id);
},
resolve() {
if (require.resolveWeak) {
return require.resolveWeak(\`./ModA\`);
}
return eval('require.resolve')(\`./ModA\`);
}
});"
`;
exports[`plugin simple import should work with template literal 1`] = `
"import loadable from '@loadable/component';
loadable({
Expand Down
36 changes: 21 additions & 15 deletions packages/babel-plugin/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ const properties = [

const LOADABLE_COMMENT = '#__LOADABLE__'

const loadablePlugin = declare((api, { defaultImportSpecifier = 'loadable' }) => {
const loadablePlugin = declare((api, {
signatures = []
}) => {
if (!signatures.find(sig => sig.from == '@loadable/component')) {
signatures.push({name: 'default', from: '@loadable/component'})
}
const { types: t } = api

function collectImportCallPaths(startPath) {
Expand All @@ -35,9 +40,9 @@ const loadablePlugin = declare((api, { defaultImportSpecifier = 'loadable' }) =>

const propertyFactories = properties.map(init => init(api))

function isValidIdentifier(path, loadableImportSpecifier, lazyImportSpecifier) {
// `loadable()`
if (loadableImportSpecifier && path.get('callee').isIdentifier({ name: loadableImportSpecifier })) {
function isValidIdentifier(path, loadableImportSpecifiers, lazyImportSpecifier) {
// loadable signatures
if (loadableImportSpecifiers.find(specifier => path.get('callee').isIdentifier({ name: specifier }))) {
return true
}

Expand All @@ -48,9 +53,8 @@ const loadablePlugin = declare((api, { defaultImportSpecifier = 'loadable' }) =>

// `loadable.lib()`
return (
loadableImportSpecifier &&
path.get('callee').isMemberExpression() &&
path.get('callee.object').isIdentifier({ name: loadableImportSpecifier }) &&
loadableImportSpecifiers.find(specifier => path.get('callee.object').isIdentifier({ name: specifier })) &&
path.get('callee.property').isIdentifier({ name: 'lib' })
)
}
Expand Down Expand Up @@ -119,28 +123,30 @@ const loadablePlugin = declare((api, { defaultImportSpecifier = 'loadable' }) =>
visitor: {
Program: {
enter(programPath) {
let loadableImportSpecifier = defaultImportSpecifier
let lazyImportSpecifier = false
const loadableSpecifiers = []

programPath.traverse({
ImportDefaultSpecifier(path) {
if (!loadableImportSpecifier) {
const { parent } = path
const { local } = path.node
loadableImportSpecifier = parent.source.value == '@loadable/component' &&
local && local.name
const { parent } = path
const { local } = path.node
if (local && signatures.find(signature => signature.name === 'default' && parent.source.value === signature.from)) {
loadableSpecifiers.push(local.name)
}
},
ImportSpecifier(path) {
const { parent } = path
const { imported, local } = path.node
if (!lazyImportSpecifier) {
const { parent } = path
const { imported, local } = path.node
lazyImportSpecifier = parent.source.value == '@loadable/component' &&
imported && imported.name == 'lazy' && local && local.name
}
if (local && imported && signatures.find(signature => imported.name === signature.name && parent.source.value === signature.from)) {
loadableSpecifiers.push(local.name)
}
},
CallExpression(path) {
if (!isValidIdentifier(path, loadableImportSpecifier, lazyImportSpecifier)) return
if (!isValidIdentifier(path, loadableSpecifiers, lazyImportSpecifier)) return
transformImport(path)
},
'ArrowFunctionExpression|FunctionExpression|ObjectMethod': path => {
Expand Down
Loading

0 comments on commit e18e37a

Please sign in to comment.