diff --git a/package-lock.json b/package-lock.json index d4273da..2c105a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,6 +35,7 @@ "release-it": "^14.13.1", "rimraf": "^3.0.2", "rollup": "^2.70.1", + "rollup-plugin-multi-input": "^1.3.1", "semver": "^7.3.5", "typescript": "^4.6.2" }, @@ -13939,6 +13940,17 @@ "yarn": ">=1.0.0" } }, + "node_modules/rollup-plugin-multi-input": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-multi-input/-/rollup-plugin-multi-input-1.3.1.tgz", + "integrity": "sha512-bPsxHR6dUney7zsCAAlfkq7lbuy5xph2CvUstSv88oqhtRiLWXwVjiA1Gb4HVjC6I9sJI2eZeQlozXa+GXJKDA==", + "dev": true, + "dependencies": { + "core-js": "^3.1.3", + "fast-glob": "^3.0.0", + "lodash": "^4.17.11" + } + }, "node_modules/rollup-plugin-rebase": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/rollup-plugin-rebase/-/rollup-plugin-rebase-4.0.1.tgz", @@ -26668,6 +26680,17 @@ "@babel/runtime": "^7.14.0" } }, + "rollup-plugin-multi-input": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-multi-input/-/rollup-plugin-multi-input-1.3.1.tgz", + "integrity": "sha512-bPsxHR6dUney7zsCAAlfkq7lbuy5xph2CvUstSv88oqhtRiLWXwVjiA1Gb4HVjC6I9sJI2eZeQlozXa+GXJKDA==", + "dev": true, + "requires": { + "core-js": "^3.1.3", + "fast-glob": "^3.0.0", + "lodash": "^4.17.11" + } + }, "rollup-plugin-rebase": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/rollup-plugin-rebase/-/rollup-plugin-rebase-4.0.1.tgz", diff --git a/package.json b/package.json index 2a823ae..400aec5 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "release-it": "^14.13.1", "rimraf": "^3.0.2", "rollup": "^2.70.1", + "rollup-plugin-multi-input": "^1.3.1", "semver": "^7.3.5", "typescript": "^4.6.2" } diff --git a/src/index.js b/src/index.js index 05bb427..cc2c8e3 100644 --- a/src/index.js +++ b/src/index.js @@ -11,6 +11,8 @@ import postcssSugarSS from "sugarss" import { createFilter } from "@rollup/pluginutils" import { getHash } from "asset-hash" +const MULTI_INPUT_PLUGIN_NAME = 'rollup-plugin-multi-input'; + const scriptExtensions = /^\.(json|mjs|js|jsx|ts|tsx)$/ const styleParser = { @@ -21,22 +23,22 @@ const styleParser = { ".sass": postcssSass } -function getPostCssPlugins(keepName) { +function getPostCssPlugins(keepName, useHash) { return [ postcssImport(), postcssSmartAsset({ url: "copy", - useHash: true, - keepName + useHash, + keepName, }) ] } /* eslint-disable max-params */ -async function processStyle(id, fileDest, keepName) { +async function processStyle(id, fileDest, keepName, useHash) { const content = await fs.readFile(id) const parser = styleParser[path.extname(id)] - const processor = postcss(getPostCssPlugins(keepName)) + const processor = postcss(getPostCssPlugins(keepName, useHash)) const text = content.toString() const result = await processor.process(text, { @@ -61,7 +63,8 @@ export default function rebase(options = {}) { exclude, verbose = false, keepName = false, - assetFolder = "" + useHash = true, + assetFolder = "", } = options const filter = createFilter(include, exclude) @@ -70,6 +73,7 @@ export default function rebase(options = {}) { const files = {} let root = null + let hasMultiInput = false; function rootRelative(file) { // Last sequence is for Windows support @@ -94,12 +98,20 @@ export default function rebase(options = {}) { return Boolean(files[fileSource]) }, + options({ plugins }) { + // Check whether we are using rollup-plugin-multi-input + hasMultiInput = !!plugins?.some(plugin => plugin != null && typeof plugin !== "boolean" && plugin.name === MULTI_INPUT_PLUGIN_NAME); + }, + /* eslint-disable complexity, max-statements */ async resolveId(importee, importer) { // Ignore root files which are typically script files. Delegate to other // plugins or default behavior. if (!importer) { - root = path.dirname(path.resolve(importee)) + // If multiInput is detected, set root once for proper paths + if (!hasMultiInput || (hasMultiInput && !root)) { + root = path.dirname(path.resolve(importee)) + } return null } @@ -142,10 +154,17 @@ export default function rebase(options = {}) { } const fileName = path.basename(importee, fileExt) - const fileHash = await getHash(fileSource) - const fileTarget = keepName - ? `${fileName}~${fileHash}${fileExt}` - : `${fileHash}${fileExt}` + + // Set default file target name + let fileTarget = `${fileName}${fileExt}`; + + if (useHash) { + // If using hash, decide whether we should keep name or hash only + const fileHash = await getHash(fileSource); + fileTarget = keepName + ? `${fileName}~${fileHash}${fileExt}` + : `${fileHash}${fileExt}`; + } // Registering for our copying job when the bundle is created (kind of a job queue) // and respect any sub folder given by the configuration options. @@ -208,7 +227,7 @@ export default function rebase(options = {}) { ) } - await processStyle(fileSource, fileDest, keepName) + await processStyle(fileSource, fileDest, keepName, useHash) } else { if (verbose) { console.log( diff --git a/test/multi-input/__snapshots__/index.test.js.snap b/test/multi-input/__snapshots__/index.test.js.snap new file mode 100644 index 0000000..e87d4d1 --- /dev/null +++ b/test/multi-input/__snapshots__/index.test.js.snap @@ -0,0 +1,28 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Multi input support 1`] = ` +"HDWIGpiP.md +SmVOQyTt.png +index.js +sub" +`; + +exports[`Multi input support 2`] = ` +"import index from './sub/folder/child.js'; +export { default } from './sub/folder/child.js'; +import './SmVOQyTt.png'; +import './HDWIGpiP.md'; +" +`; + +exports[`Multi input support 3`] = ` +"import image from '../../SmVOQyTt.png'; +import content from '../../HDWIGpiP.md'; + +function index() { + return content + image +} + +export { index as default }; +" +`; diff --git a/test/multi-input/index.test.js b/test/multi-input/index.test.js new file mode 100644 index 0000000..9271197 --- /dev/null +++ b/test/multi-input/index.test.js @@ -0,0 +1,19 @@ +import { bundleWithMultiInput, clean, list, read } from "../util" +import { join, sep } from "path" + +const root = __dirname + +test("Multi input support", async () => { + await bundleWithMultiInput(root, ['src/**/!(*.test).js'], "output", {}, { + transformOutputPath: (output) => { + // Need to transform output as bundle path starts from root of project + return output.replace(`${join('test', 'multi-input', 'src')}${sep}`, ''); + }, + }) + + expect(await list(root, "output")).toMatchSnapshot() + expect(await read(root, "output/index.js")).toMatchSnapshot() + expect(await read(root, "output/sub/folder/child.js")).toMatchSnapshot() + + await clean(root, "output") +}) diff --git a/test/multi-input/src/image.png b/test/multi-input/src/image.png new file mode 100644 index 0000000..c5916f2 Binary files /dev/null and b/test/multi-input/src/image.png differ diff --git a/test/multi-input/src/index.js b/test/multi-input/src/index.js new file mode 100644 index 0000000..aa0734b --- /dev/null +++ b/test/multi-input/src/index.js @@ -0,0 +1,3 @@ +import index from "./sub/folder/child" + +export default index diff --git a/test/multi-input/src/sub/folder/child.js b/test/multi-input/src/sub/folder/child.js new file mode 100644 index 0000000..7b67167 --- /dev/null +++ b/test/multi-input/src/sub/folder/child.js @@ -0,0 +1,6 @@ +import image from "../../image.png" +import content from "./content.md" + +export default function() { + return content + image +} diff --git a/test/multi-input/src/sub/folder/content.md b/test/multi-input/src/sub/folder/content.md new file mode 100644 index 0000000..fec5601 --- /dev/null +++ b/test/multi-input/src/sub/folder/content.md @@ -0,0 +1 @@ +# Hello diff --git a/test/no-use-hash/__snapshots__/index.test.js.snap b/test/no-use-hash/__snapshots__/index.test.js.snap new file mode 100644 index 0000000..3b84539 --- /dev/null +++ b/test/no-use-hash/__snapshots__/index.test.js.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`No use hash 1`] = ` +"content.md +image.png +index.js" +`; + +exports[`No use hash 2`] = ` +"import content from './content.md'; +import image from './image.png'; + +function index() { + return content + image +} + +export { index as default }; +" +`; diff --git a/test/no-use-hash/content.md b/test/no-use-hash/content.md new file mode 100644 index 0000000..fec5601 --- /dev/null +++ b/test/no-use-hash/content.md @@ -0,0 +1 @@ +# Hello diff --git a/test/no-use-hash/image.png b/test/no-use-hash/image.png new file mode 100644 index 0000000..c5916f2 Binary files /dev/null and b/test/no-use-hash/image.png differ diff --git a/test/no-use-hash/index.js b/test/no-use-hash/index.js new file mode 100644 index 0000000..eb32e25 --- /dev/null +++ b/test/no-use-hash/index.js @@ -0,0 +1,6 @@ +import content from "./content.md" +import image from "./image.png" + +export default function() { + return content + image +} diff --git a/test/no-use-hash/index.test.js b/test/no-use-hash/index.test.js new file mode 100644 index 0000000..91b8703 --- /dev/null +++ b/test/no-use-hash/index.test.js @@ -0,0 +1,14 @@ +import { bundle, clean, list, read } from "../util" + +const root = __dirname + +test("No use hash", async () => { + await bundle(root, "index.js", "output/index.js", { + useHash: false, + }) + + expect(await list(root, "output")).toMatchSnapshot() + expect(await read(root, "output/index.js")).toMatchSnapshot() + + await clean(root, "output") +}) diff --git a/test/util.js b/test/util.js index 00c3b52..9665ca3 100644 --- a/test/util.js +++ b/test/util.js @@ -3,6 +3,7 @@ import { readdir } from "fs" import fs from "fs-extra" import { rollup } from "rollup" +import multiInput from "rollup-plugin-multi-input"; import rebasePlugin from "../src" @@ -21,6 +22,23 @@ export async function bundle(root, input, output, pluginOptions = {}) { }) } +export async function bundleWithMultiInput(root, input, output, pluginOptions = {}, multiInputOptions = {}) { + const plugins = [ + multiInput(multiInputOptions), + rebasePlugin(pluginOptions) + ]; + + const result = await rollup({ + input: input.map(i => join(root, i)), + plugins, + }) + + await result.write({ + format: "es", + dir: join(root, output), + }) +} + export async function clean(root, files) { const input = Array.isArray(files) ? files : [ files ] const tasks = input.map((file) => fs.remove(join(root, file)))