diff --git a/README.md b/README.md index f779a00..d8e32c4 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,7 @@ This plugin is in an early state. As such not everything that is supported yet, ### Not (yet/properly) supported +- Sourcemaps (inlined script) (dev-note: we're already including magic-string for this, but do not use it yet, neeeds refactoring) - Plugins importing CSS files - CommonJS (cjs) and IIFI output formats. (Is UMD actually ever used?) - Overriding which DOM-nodes and resulting URLS to ignore/include (in a clean way) diff --git a/package.json b/package.json index 8e5fa88..334abb4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rollup-plugin-html-entry2", - "version": "0.0.2", + "version": "0.0.3", "description": "Teaches rollup how to deal with HTML, allows to use HTML-files as entry-points.", "license": "MIT", "repository": { @@ -56,7 +56,8 @@ }, "dependencies": { "@rollup/pluginutils": "^5.0.1", - "parse5": "^7.1.2" + "parse5": "^7.1.2", + "magic-string": "^0.30.0" }, "devDependencies": { "@types/node": "^18.15.11", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 24b5f54..5362db5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,9 @@ dependencies: '@rollup/pluginutils': specifier: ^5.0.1 version: 5.0.2(rollup@3.20.3) + magic-string: + specifier: ^0.30.0 + version: 0.30.0 parse5: specifier: ^7.1.2 version: 7.1.2 @@ -475,7 +478,6 @@ packages: /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - dev: true /@jridgewell/trace-mapping@0.3.18: resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} @@ -2139,6 +2141,13 @@ packages: yallist: 4.0.0 dev: true + /magic-string@0.30.0: + resolution: {integrity: sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: false + /make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} diff --git a/src/index.ts b/src/index.ts index 09d2973..8885d0e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -106,7 +106,8 @@ export default function html(opts: RollupHtmlOptions = {}): Plugin { } } }, - load: { + load: { // TODO, not in the mood to fix this. Load-result is getting cached and that gives us issues. Seperate load/transform behavior and adapt to use magic string for transformations? + // Something to figure out: its counter intuitive that rollup expects the load-callback to already return JS. It implies we already do transformations and can't really use rollup to further transform any of it. (i.e handlebars > intermediate-html > html would not be possible?) async handler(id: string) { if(virtualSources.has(id)) return virtualSources.get(id); if(!filter(id)) return; @@ -121,7 +122,7 @@ export default function html(opts: RollupHtmlOptions = {}): Plugin { }) : contents; // Parse document and store it (TODO: check for watch mode, we should check if it needs reparsing or not) - const document = htmlModule.document = htmlModule.document ?? parseHtml(htmlSrc); + const document = htmlModule.document = parseHtml(htmlSrc); // Figure out which references to load from this HTML by iterating all nodes (looking for src or href attributes) let htmlImports: HtmlImport[] = htmlModule.imports = []; diff --git a/src/loader.ts b/src/loader.ts index 5d4b75e..5075b61 100644 --- a/src/loader.ts +++ b/src/loader.ts @@ -49,6 +49,8 @@ export function makeInlineId(sourceId: string, node: DefaultTreeAdapterMap['chil export function makeLoader(mappings: NodeMapping[] = defaultMapping){ const fn : LoadNodeCallback = async function ({node, sourceId}, load){ for(const mapping of mappings){ + + // Test the mapping for a match if (mapping.tagName && mapping.tagName !== node.tagName) continue; // No match, skip if (mapping.match){ if(typeof(mapping.match) === 'function'){ @@ -67,7 +69,10 @@ export function makeLoader(mappings: NodeMapping[] = defaultMapping){ } } } + + // If we've gotten this far its a valid mapping. (either inline or a src/href attribute) if((mapping).attr){ + // Mapped on attribute, resolve its src or href (or whatever was returned) const attr = node.attrs.find(attr=>attr.name === (mapping).attr); if(!attr) continue ;// No match, skip const placeholder = await load({ @@ -76,6 +81,7 @@ export function makeLoader(mappings: NodeMapping[] = defaultMapping){ }); attr.value = placeholder; }else if((mapping).body){ + // Mapped as body, use the contents of the DOM element const body = serializeHtml(node); // unlike what you' might expect, this doesn't serialize the + + diff --git a/test/watch/fixtures/watched-file.js b/test/watch/fixtures/watched-file.js new file mode 100644 index 0000000..3616030 --- /dev/null +++ b/test/watch/fixtures/watched-file.js @@ -0,0 +1,3 @@ + + export const a = 1; // DO NOT CHANGE ME HERE, but in ../test.js + \ No newline at end of file diff --git a/test/watch/snapshots/test.js.md b/test/watch/snapshots/test.js.md new file mode 100644 index 0000000..1ae170e --- /dev/null +++ b/test/watch/snapshots/test.js.md @@ -0,0 +1,53 @@ +# Snapshot report for `test/watch/test.js` + +The actual snapshot is saved in `test.js.snap`. + +Generated by [AVA](https://avajs.dev). + +## watch + +> Snapshot 1 + + [ + { + code: `const a = 2; // If i show up as a changed file, then the watch test has gone wrong!␊ + ␊ + export { a };␊ + //# sourceMappingURL=watched-file-8c4729c5.js.map␊ + `, + fileName: 'watched-file-8c4729c5.js', + map: SourceMap { + file: 'watched-file-8c4729c5.js', + mappings: 'AACgB,MAAC,CAAC,GAAG,EAAE;;;;', + names: [], + sources: [ + '../watched-file.js', + ], + sourcesContent: [ + `␊ + export const a = 2; // If i show up as a changed file, then the watch test has gone wrong!␊ + `, + ], + version: 3, + }, + source: undefined, + }, + { + code: undefined, + fileName: 'watched-file-8c4729c5.js.map', + map: undefined, + source: '{"version":3,"file":"watched-file-8c4729c5.js","sources":["../watched-file.js"],"sourcesContent":["\\n export const a = 2; // If i show up as a changed file, then the watch test has gone wrong!\\n "],"names":[],"mappings":"AACgB,MAAC,CAAC,GAAG,EAAE;;;;"}', + }, + { + code: undefined, + fileName: 'index.html', + map: undefined, + source: `␊ + ␊ + ␊ + ␊ + ␊ + ␊ + `, + }, + ] diff --git a/test/watch/snapshots/test.js.snap b/test/watch/snapshots/test.js.snap new file mode 100644 index 0000000..949ee1a Binary files /dev/null and b/test/watch/snapshots/test.js.snap differ diff --git a/test/watch/test.js b/test/watch/test.js new file mode 100644 index 0000000..224d3e4 --- /dev/null +++ b/test/watch/test.js @@ -0,0 +1,97 @@ +import {join, dirname} from "node:path"; + +import test from "ava"; +import * as rollup from "rollup"; +import {debugPrintOutput, getCode} from "../util/test.js"; +import {resolve} from "node:path"; +import {writeFile} from "node:fs/promises"; + +import html from "../../src/index.ts"; + +const output = { + dir: 'output', // Output all files + format: 'es', // iifi and cjs should be added to tests + sourcemap: true,// Test if #sourcemapUrl is not accidentally included in the html-output +}; + +import {fileURLToPath} from "node:url"; +import {pathToFileURL} from "url"; +const __dirname = dirname(fileURLToPath(import.meta.url)); +process.chdir(join(__dirname, 'fixtures')); + + +test.serial('watch', async (t) => { + const origContent = ` + export const a = 1; // DO NOT CHANGE ME HERE, but in ../test.js + `; + const changeContent = ` + export const a = 2; // If i show up as a changed file, then the watch test has gone wrong! + ` + + const path = resolve(__dirname, 'fixtures/watched-file.js'); + await writeFile(path, origContent, {encoding: 'utf-8'}); + + const watcher = rollup.watch({ + input: 'index.html', + output, + plugins: [ + html({ + }), + ], + watch: { + skipWrite: true, + } + }); + + const steps = [ + async (bundle)=>{ + await writeFile(path, changeContent, {encoding: 'utf-8'}); + // Just wait on the watch mode to pick up on the changes + }, + async (bundle)=>{ + const code = await getCode(bundle, output, true); + debugPrintOutput('watch',code); + + // Reset the source file + await writeFile(path, origContent, {encoding: 'utf-8'}); + + // Assert the output is what we exapect; + t.snapshot(code); + + watcher + }, + ]; + + await new Promise((resolve, reject)=>{ + watcher.on('event', async (event) => { + const {result} = event; + switch (event.code) { + case "START": + t.log(`WATCH STARTED`); + break; + case "BUNDLE_START": + t.log(`REBUILDING...`); + + break; + case "BUNDLE_END": + t.log(`Rebuilt...`); + const cb = steps.shift(); + + const generated = await result.generate(output); + const cbResult = await cb(result); + if(steps.length===0){ + watcher.close(); + resolve(); + } + + break; + case "ERROR": + reject(event.error); + break; + } + if (result) { + result.close(); + } + }); + }); +});