Skip to content

Commit

Permalink
split up import/export rewrites and add some tests for logic
Browse files Browse the repository at this point in the history
fix a bug too in the process
  • Loading branch information
okwolf committed Sep 13, 2018
1 parent ca2d008 commit a04e468
Show file tree
Hide file tree
Showing 12 changed files with 217 additions and 75 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules
node_modules
coverage
9 changes: 9 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@
"args": ["--port=3000", "--docRoot=static", "--scriptRoot=js"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
{
"type": "node",
"request": "launch",
"name": "Tests",
"program": "${workspaceFolder}/node_modules/.bin/tead",
"args": ["--watch"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}
14 changes: 9 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@
"srvs": "./src/index.js"
},
"devDependencies": {
"eslint": "=5.4.0"
"eslint": "=5.4.0",
"tead": "=0.5.0"
},
"scripts": {
"format": "npx prettier --write '{src,examples,!(node_modules)/}/**/*.js'",
"format:check": "prettier --list-different '{src,examples,!(node_modules)/}/**/*.js'",
"clean": "npx rimraf coverage node_modules",
"format": "npx prettier --write '{src,examples,!(node_modules|coverage)/}/**/*.js'",
"format:check": "prettier --list-different '{src,examples,!(node_modules|coverage)/}/**/*.js'",
"lint": "eslint src/**/*.js",
"check": "npm run format:check && npm run lint",
"release": "./pre-flight-tests && npm run check && git tag $npm_package_version && git push && git push --tags && npm publish"
"test": "tead --coverage",
"check": "npm run format:check && npm run lint && npm t",
"prepare": "npm run check",
"release": "./pre-flight-tests && npm run clean && npm i && git tag $npm_package_version && git push && git push --tags && npm publish"
},
"eslintConfig": {
"extends": "eslint:recommended",
Expand Down
43 changes: 43 additions & 0 deletions src/server/getImportInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const fs = require("fs");
const path = require("path");

module.exports = ({ importPath, searchPath }) => {
const nodeModulesPath = path.resolve(searchPath, "node_modules");
const moduleName = importPath
.split("/")
.slice(0, importPath.startsWith("@") ? 2 : 1)
.join("/");
const projectPackagePath = path.resolve(searchPath, "package.json");
delete require.cache[projectPackagePath];
const projectPackage = require(projectPackagePath);
const moduleVersion = (projectPackage.dependencies || {})[moduleName];
const installedModulePath = path.resolve(nodeModulesPath, moduleName);
const isInstalled = fs.existsSync(installedModulePath);
let resolvedImportPath;

if (moduleName === importPath) {
try {
const modulePackagePath = path.resolve(
installedModulePath,
"package.json"
);
delete require.cache[modulePackagePath];
const { module } = require(modulePackagePath);
resolvedImportPath = path.resolve(installedModulePath, module);
} catch (e) {
// not installed
}
} else {
resolvedImportPath = require.resolve(importPath, {
paths: [nodeModulesPath]
});
}

return {
moduleName,
moduleVersion,
installedModulePath,
resolvedImportPath,
isInstalled
};
};
33 changes: 33 additions & 0 deletions src/server/rewriteImportsAndExports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const path = require("path");
const getImportInfo = require("./getImportInfo");
const normalizePath = require("../normalizePath");

const ES6_IMPORT_REGEX = /(import[\s\S]+?from)\s+?['"]([^"']+)["']?;?/g;
const ES6_EXPORT_REGEX = /(export[\s\S]+?from)\s+?['"]([^"']+)["']?;?/g;

module.exports = ({ contents = "", searchPath = "", importContext = "" }) =>
contents
.replace(ES6_IMPORT_REGEX, (match, imports, module) => {
if (module.startsWith(".")) {
return match;
}
const { isInstalled, moduleName, moduleVersion } = getImportInfo({
importPath: module,
searchPath
});
if (isInstalled) {
return `${imports} "/node_modules/${module}"`;
}
return `${imports} "https://unpkg.com/${moduleName}${
moduleVersion ? `@${moduleVersion}` : ""
}?module"`;
})
.replace(ES6_EXPORT_REGEX, (_, exports, module) => {
const resolvedRelativeImport =
(importContext && path.dirname(module) !== ".") ||
!path.basename(module).includes(".")
? normalizePath(path.join(importContext, module))
: module;
const importPrefix = resolvedRelativeImport.startsWith(".") ? "" : ".";
return `${exports} "${importPrefix}${resolvedRelativeImport}"`;
});
73 changes: 4 additions & 69 deletions src/server/streamPath.js
Original file line number Diff line number Diff line change
@@ -1,52 +1,12 @@
const fs = require("fs");
const path = require("path");
const stream = require("stream");
const getImportInfo = require("./getImportInfo");
const rewriteImportsAndExports = require("./rewriteImportsAndExports");
const normalizePath = require("../normalizePath");
const mimeLookup = require("./mimeLookup");

const ES6_IMPORT_REGEX = /(import[\s\S]+?from)\s+?['"]([^"']+)["']?;?/g;
const ES6_EXPORT_REGEX = /(export[\s\S]+?from)\s+?['"]([^"']+)["']?;?/g;
const NODE_MODULES_REGEX = /^\/node_modules\//;

const getImportInfo = ({ importPath, searchPath }) => {
const nodeModulesPath = path.resolve(searchPath, "node_modules");
const moduleName = importPath
.split("/")
.slice(0, importPath.startsWith("@") ? 2 : 1)
.join("/");
const projectPackagePath = path.resolve(searchPath, "package.json");
delete require.cache[projectPackagePath];
const projectPackage = require(projectPackagePath);
const moduleVersion = (projectPackage.dependencies || {})[moduleName];
const installedModulePath = path.resolve(nodeModulesPath, moduleName);
let resolvedImportPath;

if (moduleName === importPath) {
try {
const modulePackagePath = path.resolve(
installedModulePath,
"package.json"
);
delete require.cache[modulePackagePath];
const { module } = require(modulePackagePath);
resolvedImportPath = path.resolve(installedModulePath, module);
} catch (e) {
// not installed
}
} else {
resolvedImportPath = require.resolve(importPath, {
paths: [nodeModulesPath]
});
}

return {
moduleName,
moduleVersion,
installedModulePath,
resolvedImportPath
};
};

const rewrite = (rewritter = chunk => chunk) =>
new stream.Transform({
transform(chunk, _, next) {
Expand All @@ -57,33 +17,8 @@ const rewrite = (rewritter = chunk => chunk) =>
});

const rewriteScript = ({ searchPath, importContext }) =>
rewrite(chunk =>
chunk
.replace(ES6_IMPORT_REGEX, (match, imports, module) => {
if (module.startsWith(".")) {
return match;
}
const {
moduleName,
moduleVersion,
installedModulePath
} = getImportInfo({
importPath: module,
searchPath
});
if (fs.existsSync(installedModulePath)) {
return `${imports} "/node_modules/${module}"`;
}
return `${imports} "https://unpkg.com/${moduleName}${
moduleVersion ? `@${moduleVersion}` : ""
}?module"`;
})
.replace(ES6_EXPORT_REGEX, (_, exports, module) => {
const resolvedRelativeImport = importContext
? normalizePath(path.join(importContext, module))
: module;
return `${exports} ".${resolvedRelativeImport}"`;
})
rewrite(contents =>
rewriteImportsAndExports({ contents, searchPath, importContext })
);

const streamFile = ({
Expand Down
1 change: 1 addition & 0 deletions test/server/fake-project/installed/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!node_modules

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions test/server/fake-project/installed/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "fake-project",
"version": "0.0.0",
"private": true,
"dependencies": {
"fake-package": "1.0.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "fake-project",
"version": "0.0.0",
"private": true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "fake-project",
"version": "0.0.0",
"private": true,
"dependencies": {
"fake-package": "1.0.0"
}
}
90 changes: 90 additions & 0 deletions test/server/rewriteImportsAndExports.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
const path = require("path");

const fakeProjectPath = path.join(__dirname, "fake-project");

const rewriteImportsAndExports = require("../../src/server/rewriteImportsAndExports");
export default {
rewriteImportsAndExports: {
"should be a function": [typeof rewriteImportsAndExports, "function"],
imports: {
"should not modify relative imports": [
rewriteImportsAndExports({
contents: `import something from "./somewhere"`
}),
`import something from "./somewhere"`
],
absolute: {
"not installed": {
"without version should rewrite to default unpkg": [
rewriteImportsAndExports({
contents: `import { app, h } from "fake-package"`,
searchPath: path.join(
fakeProjectPath,
"not-installed",
"no-version"
)
}),
`import { app, h } from "https://unpkg.com/fake-package?module"`
],
"with version should rewrite to unpkg with version": [
rewriteImportsAndExports({
contents: `import { app, h } from "fake-package"`,
searchPath: path.join(
fakeProjectPath,
"not-installed",
"with-version"
)
}),
`import { app, h } from "https://unpkg.com/fake-package@1.0.0?module"`
]
},
installed: {
"with version should rewrite to local node_modules": [
rewriteImportsAndExports({
contents: `import { app, h } from "fake-package"`,
searchPath: path.join(fakeProjectPath, "installed")
}),
`import { app, h } from "/node_modules/fake-package"`
]
}
}
},
exports: {
"should resolve relative subfolder to index without extension": [
rewriteImportsAndExports({
contents: `export { default } from "./subfolder/index"`,
importContext: "/folder"
}),
`export { default } from "./folder/subfolder/index"`
],
"should resolve relative subfolder to index with extension": [
rewriteImportsAndExports({
contents: `export { default } from "./subfolder/index.js"`,
importContext: "/folder"
}),
`export { default } from "./folder/subfolder/index.js"`
],
"should resolve relative subfolder to non index file with extension": [
rewriteImportsAndExports({
contents: `export { default } from "./somewhere/name.js"`,
importContext: "/test"
}),
`export { default } from "./test/somewhere/name.js"`
],
"should resolve relative root to non index file without extension": [
rewriteImportsAndExports({
contents: `export { default } from "./name"`,
importContext: "/folder"
}),
`export { default } from "./folder/name"`
],
"should resolve relative root to non index file with extension": [
rewriteImportsAndExports({
contents: `export { default } from "./name.js"`,
importContext: "/folder"
}),
`export { default } from "./name.js"`
]
}
}
};

0 comments on commit a04e468

Please sign in to comment.