2018-08-30 12:02:18 +00:00
|
|
|
/**
|
|
|
|
COPYRIGHT (c) 2017-present James Kyle <me@thejameskyle.com>
|
|
|
|
MIT License
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
a copy of this software and associated documentation files (the
|
|
|
|
"Software"), to deal in the Software without restriction, including
|
|
|
|
without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
|
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
|
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
|
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWAR
|
|
|
|
*/
|
2018-07-24 09:24:40 +00:00
|
|
|
// This file is https://github.com/jamiebuilds/react-loadable/blob/master/src/babel.js
|
|
|
|
// Modified to also look for `next/dynamic`
|
|
|
|
// Modified to put `webpack` and `modules` under `loadableGenerated` to be backwards compatible with next/dynamic which has a `modules` key
|
|
|
|
// Modified to support `dynamic(import('something'))` and `dynamic(import('something'), options)
|
2018-08-30 12:02:18 +00:00
|
|
|
|
2018-12-02 17:30:00 +00:00
|
|
|
import {PluginObj} from '@babel/core'
|
|
|
|
import {NodePath} from '@babel/traverse'
|
|
|
|
import * as BabelTypes from '@babel/types'
|
|
|
|
|
|
|
|
export default function ({ types: t }: {types: typeof BabelTypes}): PluginObj {
|
2018-07-24 09:24:40 +00:00
|
|
|
return {
|
|
|
|
visitor: {
|
2018-12-02 17:30:00 +00:00
|
|
|
ImportDeclaration (path: NodePath<BabelTypes.ImportDeclaration>) {
|
2018-07-24 09:24:40 +00:00
|
|
|
let source = path.node.source.value
|
2018-08-30 12:02:18 +00:00
|
|
|
if (source !== 'next/dynamic') return
|
2018-07-24 09:24:40 +00:00
|
|
|
|
|
|
|
let defaultSpecifier = path.get('specifiers').find(specifier => {
|
|
|
|
return specifier.isImportDefaultSpecifier()
|
|
|
|
})
|
|
|
|
|
|
|
|
if (!defaultSpecifier) return
|
|
|
|
|
2018-12-02 17:30:00 +00:00
|
|
|
const bindingName = defaultSpecifier.node.local.name
|
|
|
|
const binding = path.scope.getBinding(bindingName)
|
|
|
|
|
2018-12-03 13:18:52 +00:00
|
|
|
if (!binding) {
|
2018-12-02 17:30:00 +00:00
|
|
|
return
|
|
|
|
}
|
2018-07-24 09:24:40 +00:00
|
|
|
|
|
|
|
binding.referencePaths.forEach(refPath => {
|
|
|
|
let callExpression = refPath.parentPath
|
|
|
|
|
|
|
|
if (
|
|
|
|
callExpression.isMemberExpression() &&
|
2018-12-05 20:45:50 +00:00
|
|
|
callExpression.node.computed === false
|
2018-07-24 09:24:40 +00:00
|
|
|
) {
|
2018-12-05 20:45:50 +00:00
|
|
|
const property = callExpression.get('property')
|
|
|
|
if (!Array.isArray(property) && property.isIdentifier({ name: 'Map' })) {
|
|
|
|
callExpression = callExpression.parentPath
|
|
|
|
}
|
2018-07-24 09:24:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!callExpression.isCallExpression()) return
|
|
|
|
|
|
|
|
let args = callExpression.get('arguments')
|
2018-12-02 17:30:00 +00:00
|
|
|
if (args.length > 2) {
|
|
|
|
throw callExpression.buildCodeFrameError('next/dynamic only accepts 2 arguments')
|
|
|
|
}
|
2018-07-24 09:24:40 +00:00
|
|
|
|
|
|
|
if (!args[0]) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-12-02 17:30:00 +00:00
|
|
|
let loader
|
|
|
|
let options
|
|
|
|
|
2018-09-27 14:40:54 +00:00
|
|
|
if (args[0].isObjectExpression()) {
|
|
|
|
options = args[0]
|
|
|
|
} else {
|
2018-07-24 09:24:40 +00:00
|
|
|
if (!args[1]) {
|
2018-12-02 17:30:00 +00:00
|
|
|
callExpression.node.arguments.push(t.objectExpression([]))
|
2018-07-24 09:24:40 +00:00
|
|
|
}
|
2018-09-27 14:40:54 +00:00
|
|
|
// This is needed as the code is modified above
|
2018-07-24 09:24:40 +00:00
|
|
|
args = callExpression.get('arguments')
|
|
|
|
loader = args[0]
|
|
|
|
options = args[1]
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!options.isObjectExpression()) return
|
|
|
|
|
|
|
|
let properties = options.get('properties')
|
2018-12-02 17:30:00 +00:00
|
|
|
let propertiesMap: {[key: string]: NodePath<BabelTypes.ObjectProperty | BabelTypes.ObjectMethod | BabelTypes.SpreadProperty>} = {}
|
2018-07-24 09:24:40 +00:00
|
|
|
|
|
|
|
properties.forEach(property => {
|
2018-12-02 17:30:00 +00:00
|
|
|
const key: any = property.get('key')
|
2018-07-24 09:24:40 +00:00
|
|
|
propertiesMap[key.node.name] = property
|
|
|
|
})
|
|
|
|
|
|
|
|
if (propertiesMap.loadableGenerated) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (propertiesMap.loader) {
|
|
|
|
loader = propertiesMap.loader.get('value')
|
|
|
|
}
|
|
|
|
|
|
|
|
if (propertiesMap.modules) {
|
|
|
|
loader = propertiesMap.modules.get('value')
|
|
|
|
}
|
|
|
|
|
2018-12-03 13:18:52 +00:00
|
|
|
if (!loader || Array.isArray(loader)) {
|
2018-12-02 17:30:00 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
const dynamicImports: BabelTypes.StringLiteral[] = []
|
2018-07-24 09:24:40 +00:00
|
|
|
|
2018-12-02 17:30:00 +00:00
|
|
|
loader.traverse({
|
2018-07-24 09:24:40 +00:00
|
|
|
Import (path) {
|
2018-12-02 17:30:00 +00:00
|
|
|
const args = path.parentPath.get('arguments')
|
2018-12-03 13:18:52 +00:00
|
|
|
if (!Array.isArray(args)) return
|
2018-12-02 17:30:00 +00:00
|
|
|
const node: any = args[0].node
|
|
|
|
dynamicImports.push(node)
|
2018-07-24 09:24:40 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
if (!dynamicImports.length) return
|
2018-12-03 13:18:52 +00:00
|
|
|
|
2018-12-02 17:30:00 +00:00
|
|
|
options.node.properties.push(t.objectProperty(
|
|
|
|
t.identifier('loadableGenerated'),
|
|
|
|
t.objectExpression([
|
|
|
|
t.objectProperty(
|
|
|
|
t.identifier('webpack'),
|
|
|
|
t.arrowFunctionExpression(
|
|
|
|
[],
|
2018-07-24 09:24:40 +00:00
|
|
|
t.arrayExpression(
|
|
|
|
dynamicImports.map(dynamicImport => {
|
2018-12-02 17:30:00 +00:00
|
|
|
return t.callExpression(
|
|
|
|
t.memberExpression(
|
|
|
|
t.identifier('require'),
|
|
|
|
t.identifier('resolveWeak')
|
|
|
|
),
|
|
|
|
[dynamicImport]
|
|
|
|
)
|
2018-07-24 09:24:40 +00:00
|
|
|
})
|
|
|
|
)
|
|
|
|
)
|
2018-12-02 17:30:00 +00:00
|
|
|
),
|
|
|
|
t.objectProperty(
|
|
|
|
t.identifier('modules'),
|
|
|
|
t.arrayExpression(
|
|
|
|
dynamicImports
|
|
|
|
)
|
|
|
|
)
|
|
|
|
])
|
|
|
|
))
|
2018-09-25 13:27:09 +00:00
|
|
|
|
|
|
|
// Turns `dynamic(import('something'))` into `dynamic(() => import('something'))` for backwards compat.
|
|
|
|
// This is the replicate the behavior in versions below Next.js 7 where we magically handled not executing the `import()` too.
|
|
|
|
// We'll deprecate this behavior and provide a codemod for it in 7.1.
|
|
|
|
if (loader.isCallExpression()) {
|
|
|
|
const arrowFunction = t.arrowFunctionExpression([], loader.node)
|
|
|
|
loader.replaceWith(arrowFunction)
|
|
|
|
}
|
2018-07-24 09:24:40 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|