From 68738d1c904476e9c99989ae5ee12a90c184fbc1 Mon Sep 17 00:00:00 2001 From: Arunoda Susiripala Date: Wed, 16 Aug 2017 22:44:00 +0530 Subject: [PATCH] Use deterministic names for dynamic import (#2788) * Always use the same name for the same dynamic import. * Add unit tests for the modulePath generation. * Allow tests to run correctly on Windows. * Make the chunk name a bit pretty. * Fix tests to run on Windows. --- server/build/babel/plugins/handle-import.js | 26 ++++++++++-- test/unit/handle-import-babel-plugin.test.js | 42 ++++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 test/unit/handle-import-babel-plugin.test.js diff --git a/server/build/babel/plugins/handle-import.js b/server/build/babel/plugins/handle-import.js index 930effa758..164ddd5728 100644 --- a/server/build/babel/plugins/handle-import.js +++ b/server/build/babel/plugins/handle-import.js @@ -2,7 +2,8 @@ // We've added support for SSR with this version import template from 'babel-template' import syntax from 'babel-plugin-syntax-dynamic-import' -import UUID from 'uuid' +import { dirname, resolve, sep } from 'path' +import Crypto from 'crypto' const TYPE_IMPORT = 'Import' @@ -37,14 +38,33 @@ const buildImport = (args) => (template(` ) `)) +export function getModulePath (sourceFilename, moduleName) { + // resolve only if it's a local module + const modulePath = (moduleName[0] === '.') + ? resolve(dirname(sourceFilename), moduleName) : moduleName + + const cleanedModulePath = modulePath + .replace(/(index){0,1}\.js$/, '') // remove .js, index.js + .replace(/[/\\]$/, '') // remove end slash + + return cleanedModulePath +} + export default () => ({ inherits: syntax, visitor: { - CallExpression (path) { + CallExpression (path, state) { if (path.node.callee.type === TYPE_IMPORT) { const moduleName = path.node.arguments[0].value - const name = `${moduleName.replace(/[^\w]/g, '-')}-${UUID.v4()}` + const sourceFilename = state.file.opts.filename + + const modulePath = getModulePath(sourceFilename, moduleName) + const modulePathHash = Crypto.createHash('md5').update(modulePath).digest('hex') + + const relativeModulePath = modulePath.replace(`${process.cwd()}${sep}`, '') + const name = `${relativeModulePath.replace(/[^\w]/g, '-')}-${modulePathHash}` + const newImport = buildImport({ name })({ diff --git a/test/unit/handle-import-babel-plugin.test.js b/test/unit/handle-import-babel-plugin.test.js new file mode 100644 index 0000000000..48fb8a58b1 --- /dev/null +++ b/test/unit/handle-import-babel-plugin.test.js @@ -0,0 +1,42 @@ +/* global describe, it, expect */ +import { getModulePath } from '../../dist/server/build/babel/plugins/handle-import' + +function cleanPath (mPath) { + return mPath + .replace(/\\/g, '/') + .replace(/^.*:/, '') +} + +describe('handle-import-babel-plugin', () => { + describe('getModulePath', () => { + it('should not do anything to NPM modules', () => { + const mPath = getModulePath('/abc/pages/about.js', 'cool-module') + expect(mPath).toBe('cool-module') + }) + + it('should not do anything to private NPM modules', () => { + const mPath = getModulePath('/abc/pages/about.js', '@zeithq/cool-module') + expect(mPath).toBe('@zeithq/cool-module') + }) + + it('should resolve local modules', () => { + const mPath = getModulePath('/abc/pages/about.js', '../components/hello.js') + expect(cleanPath(mPath)).toBe('/abc/components/hello') + }) + + it('should remove index.js', () => { + const mPath = getModulePath('/abc/pages/about.js', '../components/c1/index.js') + expect(cleanPath(mPath)).toBe('/abc/components/c1') + }) + + it('should remove .js', () => { + const mPath = getModulePath('/abc/pages/about.js', '../components/bb.js') + expect(cleanPath(mPath)).toBe('/abc/components/bb') + }) + + it('should remove end slash', () => { + const mPath = getModulePath('/abc/pages/about.js', '../components/bb/') + expect(cleanPath(mPath)).toBe('/abc/components/bb') + }) + }) +}) -- GitLab