@rohal12/twee-ts 1.18.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -3
- package/THIRD_PARTY_NOTICES +111 -0
- package/UNLICENSE +24 -0
- package/dist/bin/twee-ts.js +705 -9447
- package/dist/bin/twee-ts.js.map +1 -1
- package/dist/diagnostic-text-Bj-UQ0Jl.cjs +27007 -0
- package/dist/diagnostic-text-Bj-UQ0Jl.cjs.map +1 -0
- package/dist/diagnostic-text-DMVZfBh9.js +26530 -0
- package/dist/diagnostic-text-DMVZfBh9.js.map +1 -0
- package/dist/index.cjs +91 -9140
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +208 -416
- package/dist/index.d.ts +208 -416
- package/dist/index.js +4 -9317
- package/dist/lint-BkpfLGuP.js +288 -0
- package/dist/lint-BkpfLGuP.js.map +1 -0
- package/dist/options-DuBkFyqi.d.cts +28 -0
- package/dist/options-nc5NpnSc.d.ts +28 -0
- package/dist/plugins/rollup.cjs +177 -0
- package/dist/plugins/rollup.cjs.map +1 -0
- package/dist/plugins/rollup.d.cts +52 -0
- package/dist/plugins/rollup.d.ts +13 -12
- package/dist/plugins/rollup.js +136 -66
- package/dist/plugins/rollup.js.map +1 -1
- package/dist/plugins/vite.cjs +2225 -0
- package/dist/plugins/vite.cjs.map +1 -0
- package/dist/plugins/vite.d.cts +15 -0
- package/dist/plugins/vite.d.ts +5 -12
- package/dist/plugins/vite.js +2120 -516
- package/dist/plugins/vite.js.map +1 -1
- package/dist/types-BvI6pg2y.d.cts +389 -0
- package/dist/types-BvI6pg2y.d.ts +389 -0
- package/dist/watch-targets-BrEYmUwU.cjs +643 -0
- package/dist/watch-targets-BrEYmUwU.cjs.map +1 -0
- package/dist/watch-targets-DzvPyWDo.js +548 -0
- package/dist/watch-targets-DzvPyWDo.js.map +1 -0
- package/package.json +80 -27
- package/schemas/twee-ts.config.schema.json +55 -17
- package/dist/index.js.map +0 -1
- package/dist/paths-BuTAg2av.js +0 -7044
- package/dist/paths-BuTAg2av.js.map +0 -1
- package/dist/types-D8Xfe0l8.d.ts +0 -71
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
|
|
2
|
+
import { n as SharedPluginOptions, t as PluginCompileOptions } from "../options-DuBkFyqi.cjs";
|
|
3
|
+
//#region src/plugins/diagnostics.d.ts
|
|
4
|
+
/** An error carrying the file and line the bundler reports (and Vite shows in its overlay). */
|
|
5
|
+
interface LocatedError extends Error {
|
|
6
|
+
id?: string;
|
|
7
|
+
loc?: {
|
|
8
|
+
file: string;
|
|
9
|
+
line: number;
|
|
10
|
+
column: number;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/plugins/paths.d.ts
|
|
15
|
+
/** The output settings that say where a bundle is written, as Rollup and Vite pass them to generateBundle. */
|
|
16
|
+
interface OutputLocation {
|
|
17
|
+
readonly dir?: string | undefined;
|
|
18
|
+
readonly file?: string | undefined;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/plugins/rollup.d.ts
|
|
22
|
+
export type TweeTsRollupPluginOptions = SharedPluginOptions;
|
|
23
|
+
export declare function tweeTsPlugin(options: TweeTsRollupPluginOptions): {
|
|
24
|
+
name: string;
|
|
25
|
+
options(inputOptions: object): undefined;
|
|
26
|
+
buildStart(this: {
|
|
27
|
+
addWatchFile: (id: string) => void;
|
|
28
|
+
warn: (message: string) => void;
|
|
29
|
+
meta: {
|
|
30
|
+
watchMode: boolean;
|
|
31
|
+
};
|
|
32
|
+
}): void;
|
|
33
|
+
closeWatcher(): void;
|
|
34
|
+
renderStart(outputOptions: OutputLocation): void;
|
|
35
|
+
generateBundle(this: {
|
|
36
|
+
emitFile: (opts: {
|
|
37
|
+
type: 'asset';
|
|
38
|
+
fileName: string;
|
|
39
|
+
source: string;
|
|
40
|
+
}) => void;
|
|
41
|
+
error: (error: LocatedError | string) => never;
|
|
42
|
+
warn: (message: string) => void;
|
|
43
|
+
}, outputOptions: OutputLocation, bundle: Readonly<Record<string, unknown>>): Promise<void>;
|
|
44
|
+
writeBundle(outputOptions: OutputLocation, bundle: Readonly<Record<string, {
|
|
45
|
+
readonly type: 'asset';
|
|
46
|
+
readonly source: string | Uint8Array;
|
|
47
|
+
} | {
|
|
48
|
+
readonly type: 'chunk';
|
|
49
|
+
}>>): void;
|
|
50
|
+
};
|
|
51
|
+
//#endregion
|
|
52
|
+
export type { PluginCompileOptions };
|
package/dist/plugins/rollup.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
import { n as SharedPluginOptions, t as PluginCompileOptions } from "../options-nc5NpnSc.js";
|
|
2
3
|
//#region src/plugins/diagnostics.d.ts
|
|
3
4
|
/** An error carrying the file and line the bundler reports (and Vite shows in its overlay). */
|
|
4
5
|
interface LocatedError extends Error {
|
|
@@ -18,25 +19,18 @@ interface OutputLocation {
|
|
|
18
19
|
}
|
|
19
20
|
//#endregion
|
|
20
21
|
//#region src/plugins/rollup.d.ts
|
|
21
|
-
export
|
|
22
|
-
/** Source directories/files to compile. */
|
|
23
|
-
sources: string[];
|
|
24
|
-
/** Story format ID. */
|
|
25
|
-
format?: string;
|
|
26
|
-
/** Output filename. Default: 'index.html'. */
|
|
27
|
-
outputFilename?: string;
|
|
28
|
-
/** Additional compile options. */
|
|
29
|
-
compileOptions?: Partial<CompileOptions>;
|
|
30
|
-
}
|
|
22
|
+
export type TweeTsRollupPluginOptions = SharedPluginOptions;
|
|
31
23
|
export declare function tweeTsPlugin(options: TweeTsRollupPluginOptions): {
|
|
32
24
|
name: string;
|
|
33
25
|
options(inputOptions: object): undefined;
|
|
34
26
|
buildStart(this: {
|
|
35
27
|
addWatchFile: (id: string) => void;
|
|
28
|
+
warn: (message: string) => void;
|
|
36
29
|
meta: {
|
|
37
30
|
watchMode: boolean;
|
|
38
31
|
};
|
|
39
32
|
}): void;
|
|
33
|
+
closeWatcher(): void;
|
|
40
34
|
renderStart(outputOptions: OutputLocation): void;
|
|
41
35
|
generateBundle(this: {
|
|
42
36
|
emitFile: (opts: {
|
|
@@ -44,8 +38,15 @@ export declare function tweeTsPlugin(options: TweeTsRollupPluginOptions): {
|
|
|
44
38
|
fileName: string;
|
|
45
39
|
source: string;
|
|
46
40
|
}) => void;
|
|
47
|
-
error: (error: LocatedError) => never;
|
|
41
|
+
error: (error: LocatedError | string) => never;
|
|
48
42
|
warn: (message: string) => void;
|
|
49
43
|
}, outputOptions: OutputLocation, bundle: Readonly<Record<string, unknown>>): Promise<void>;
|
|
44
|
+
writeBundle(outputOptions: OutputLocation, bundle: Readonly<Record<string, {
|
|
45
|
+
readonly type: 'asset';
|
|
46
|
+
readonly source: string | Uint8Array;
|
|
47
|
+
} | {
|
|
48
|
+
readonly type: 'chunk';
|
|
49
|
+
}>>): void;
|
|
50
50
|
};
|
|
51
51
|
//#endregion
|
|
52
|
+
export type { PluginCompileOptions };
|
package/dist/plugins/rollup.js
CHANGED
|
@@ -1,102 +1,172 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
* and emit nothing; warnings go through Rollup's warnings.
|
|
9
|
-
*/
|
|
1
|
+
/*! @rohal12/twee-ts (Unlicense). Bundles third-party code under its own licence: see THIRD_PARTY_NOTICES. */
|
|
2
|
+
import { G as recordOwnOutput, at as outputPaths, lt as identify } from "../diagnostic-text-DMVZfBh9.js";
|
|
3
|
+
import { c as createOutputRecord, g as fatalError, h as compileStory, l as emittedFilePath, o as resolvePluginOptions, p as outputLocations, r as watchPlan } from "../watch-targets-DzvPyWDo.js";
|
|
4
|
+
import { dirname, join, resolve, sep } from "node:path";
|
|
5
|
+
import { mkdtempSync, readlinkSync, rmSync, utimesSync, watch, writeFileSync } from "node:fs";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
//#region src/plugins/link-watch.ts
|
|
10
8
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* file or folder the build writes (`outputs`), and no link to a folder, which
|
|
17
|
-
* source discovery doesn't follow and which may lead to the output. Any other
|
|
18
|
-
* folder is replaced by what it contains, minus those, so writing the outputs
|
|
19
|
-
* starts no build. A file added straight to such a folder is found at the next
|
|
20
|
-
* build. A path the build writes to is never registered itself: Rollup fails a
|
|
21
|
-
* watch build whose watched files include its `output.dir` or `output.file`.
|
|
9
|
+
* Tells a bundler's build watch that a link the story's inputs are reached through was pointed elsewhere, when the
|
|
10
|
+
* bundler's own watcher can't. Rollup's watcher (chokidar) follows a registered link and watches the file it leads to,
|
|
11
|
+
* so replacing the link, or a hop of a chain of links, raises nothing. The link's folder is watched here instead (not
|
|
12
|
+
* recursively, so what the build writes below it is not reported), and a change of any link starts a build the way an
|
|
13
|
+
* edit of a registered file does: by touching a file the plugin has registered with the bundler.
|
|
22
14
|
*/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
15
|
+
/** How long a burst of file system events settles before the links are read again (a link is replaced in two steps). */
|
|
16
|
+
const SETTLE_MS = 30;
|
|
17
|
+
/** Where a link leads (as written), or nothing when it is no link or does not exist. */
|
|
18
|
+
function destination(link) {
|
|
26
19
|
try {
|
|
27
|
-
|
|
28
|
-
stat: statSync(path),
|
|
29
|
-
real
|
|
30
|
-
} : walkedEntry(path, real);
|
|
20
|
+
return readlinkSync(link);
|
|
31
21
|
} catch {
|
|
32
|
-
return
|
|
22
|
+
return;
|
|
33
23
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A watch of links that touches its signal file when one of them leads elsewhere, or appears or disappears. The watch
|
|
27
|
+
* is a refinement of the build watch: without a temporary folder to write the signal file in (a missing or read-only
|
|
28
|
+
* TMPDIR), there is none, and a retargeted link is read with the next build something else starts.
|
|
29
|
+
*/
|
|
30
|
+
function createLinkWatch() {
|
|
31
|
+
let folder;
|
|
32
|
+
let signal;
|
|
38
33
|
try {
|
|
39
|
-
|
|
34
|
+
folder = mkdtempSync(join(tmpdir(), "twee-ts-links-"));
|
|
35
|
+
signal = join(folder, "changed");
|
|
36
|
+
writeFileSync(signal, "");
|
|
40
37
|
} catch {
|
|
41
|
-
|
|
38
|
+
if (folder !== void 0) rmSync(folder, {
|
|
39
|
+
recursive: true,
|
|
40
|
+
force: true
|
|
41
|
+
});
|
|
42
|
+
return;
|
|
42
43
|
}
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
const signalFolder = folder;
|
|
45
|
+
let touches = 0;
|
|
46
|
+
const watchers = [];
|
|
47
|
+
let timer;
|
|
48
|
+
let known = /* @__PURE__ */ new Map();
|
|
49
|
+
const touch = () => {
|
|
50
|
+
touches += 1;
|
|
51
|
+
const at = new Date(Date.now() + touches * 1e3);
|
|
52
|
+
try {
|
|
53
|
+
writeFileSync(signal, String(touches));
|
|
54
|
+
utimesSync(signal, at, at);
|
|
55
|
+
} catch {}
|
|
56
|
+
};
|
|
57
|
+
const check = () => {
|
|
58
|
+
timer = void 0;
|
|
59
|
+
if ([...known].filter(([link, was]) => destination(link) !== was).length === 0) return;
|
|
60
|
+
known = new Map([...known.keys()].map((link) => [link, destination(link)]));
|
|
61
|
+
touch();
|
|
62
|
+
};
|
|
63
|
+
/** Reads the links again once the events that come together have come. */
|
|
64
|
+
const schedule = () => {
|
|
65
|
+
clearTimeout(timer);
|
|
66
|
+
timer = setTimeout(check, SETTLE_MS);
|
|
67
|
+
};
|
|
68
|
+
const stopWatching = () => {
|
|
69
|
+
for (const watcher of watchers.splice(0)) watcher.close();
|
|
70
|
+
clearTimeout(timer);
|
|
71
|
+
timer = void 0;
|
|
72
|
+
};
|
|
73
|
+
const onExit = () => {
|
|
74
|
+
rmSync(signalFolder, {
|
|
75
|
+
recursive: true,
|
|
76
|
+
force: true
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
process.once("exit", onExit);
|
|
80
|
+
return {
|
|
81
|
+
signal,
|
|
82
|
+
watch(links) {
|
|
83
|
+
stopWatching();
|
|
84
|
+
known = new Map(links.map((link) => [link, destination(link)]));
|
|
85
|
+
for (const parent of new Set(links.map((link) => dirname(link)))) try {
|
|
86
|
+
const watcher = watch(parent, schedule);
|
|
87
|
+
watcher.on("error", schedule);
|
|
88
|
+
watchers.push(watcher);
|
|
89
|
+
} catch {}
|
|
90
|
+
},
|
|
91
|
+
close() {
|
|
92
|
+
stopWatching();
|
|
93
|
+
process.removeListener("exit", onExit);
|
|
94
|
+
onExit();
|
|
95
|
+
}
|
|
96
|
+
};
|
|
51
97
|
}
|
|
98
|
+
//#endregion
|
|
99
|
+
//#region src/plugins/rollup.ts
|
|
100
|
+
/**
|
|
101
|
+
* Ready-made Rollup plugin for twee-ts.
|
|
102
|
+
* Compiles .tw files and emits HTML as an asset. Compile errors fail the build
|
|
103
|
+
* and emit nothing; warnings go through Rollup's warnings.
|
|
104
|
+
*/
|
|
52
105
|
function tweeTsPlugin(options) {
|
|
53
|
-
const
|
|
106
|
+
const resolved = resolvePluginOptions("rollup", options);
|
|
107
|
+
const { outputFilename } = resolved;
|
|
54
108
|
const record = createOutputRecord(outputFilename);
|
|
109
|
+
let linkWatch;
|
|
110
|
+
let linkWatchUnavailable = false;
|
|
111
|
+
const followed = /* @__PURE__ */ new Map();
|
|
55
112
|
return {
|
|
56
113
|
name: "twee-ts",
|
|
57
114
|
options(inputOptions) {
|
|
58
|
-
const
|
|
115
|
+
const output = Reflect.get(inputOptions, "output");
|
|
59
116
|
for (const location of outputLocations(output)) record.addLocation(location);
|
|
60
117
|
},
|
|
61
118
|
buildStart() {
|
|
62
119
|
if (!this.meta.watchMode) return;
|
|
63
|
-
const outputs = outputPaths(record.outputs());
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
120
|
+
const outputs = outputPaths(record.outputs(), "snapshot");
|
|
121
|
+
const plan = watchPlan(resolved.inputs, resolved.excluded, outputs, "given");
|
|
122
|
+
const targets = plan.targets.map((target) => identify(target).absolute);
|
|
123
|
+
for (const target of targets) this.addWatchFile(target);
|
|
124
|
+
for (const [path, real] of plan.reals) {
|
|
125
|
+
const first = followed.get(path) ?? real;
|
|
126
|
+
followed.set(path, first);
|
|
127
|
+
if (first !== real) this.addWatchFile(resolve(real));
|
|
128
|
+
}
|
|
129
|
+
if (plan.links.length > 0 && !linkWatchUnavailable) {
|
|
130
|
+
linkWatch ??= createLinkWatch();
|
|
131
|
+
if (linkWatch === void 0) {
|
|
132
|
+
linkWatchUnavailable = true;
|
|
133
|
+
this.warn("[twee-ts] The link watch is unavailable because no temporary folder can be written (check TMPDIR); a link pointed at another target is read with the next build something else starts.");
|
|
134
|
+
} else {
|
|
135
|
+
linkWatch.watch(plan.links);
|
|
136
|
+
this.addWatchFile(linkWatch.signal);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const unwatchable = sep === "/" ? targets.filter((target) => target.includes("\\")) : [];
|
|
140
|
+
if (unwatchable.length > 0) this.warn(`[twee-ts] Rollup's watch mode cannot watch ${unwatchable.map((target) => JSON.stringify(target)).join(", ")} because the name contains a backslash; edits to it will not rebuild the story.`);
|
|
141
|
+
},
|
|
142
|
+
closeWatcher() {
|
|
143
|
+
linkWatch?.close();
|
|
144
|
+
linkWatch = void 0;
|
|
71
145
|
},
|
|
72
146
|
renderStart(outputOptions) {
|
|
73
147
|
record.addLocation(outputOptions);
|
|
74
148
|
},
|
|
75
149
|
async generateBundle(outputOptions, bundle) {
|
|
150
|
+
if (Object.hasOwn(bundle, outputFilename)) return this.error(`twee-ts: the bundle already holds a file named ${outputFilename}; set the plugin's outputFilename to another name.`);
|
|
76
151
|
record.addLocation(outputOptions);
|
|
77
152
|
record.addFiles(outputOptions, Object.keys(bundle));
|
|
78
|
-
let
|
|
153
|
+
let story;
|
|
79
154
|
try {
|
|
80
|
-
|
|
81
|
-
sources: options.sources,
|
|
82
|
-
formatId: options.format,
|
|
83
|
-
...options.compileOptions
|
|
84
|
-
}, record.outputs());
|
|
155
|
+
story = await compileStory(resolved.compile(), record.outputs(), void 0, true);
|
|
85
156
|
} catch (e) {
|
|
86
157
|
return this.error(fatalError(e));
|
|
87
158
|
}
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
warnings = splitDiagnostics(result);
|
|
91
|
-
} catch (e) {
|
|
92
|
-
return this.error(e);
|
|
93
|
-
}
|
|
94
|
-
for (const w of warnings) this.warn(formatDiagnostic(w));
|
|
159
|
+
for (const warning of story.warnings) this.warn(warning);
|
|
95
160
|
this.emitFile({
|
|
96
161
|
type: "asset",
|
|
97
162
|
fileName: outputFilename,
|
|
98
|
-
source:
|
|
163
|
+
source: story.output
|
|
99
164
|
});
|
|
165
|
+
},
|
|
166
|
+
writeBundle(outputOptions, bundle) {
|
|
167
|
+
const asset = bundle[outputFilename];
|
|
168
|
+
const destination = emittedFilePath(outputOptions, outputFilename);
|
|
169
|
+
if (asset?.type === "asset" && destination !== void 0) recordOwnOutput(destination, asset.source);
|
|
100
170
|
}
|
|
101
171
|
};
|
|
102
172
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rollup.js","names":[],"sources":["../../src/plugins/rollup.ts"],"sourcesContent":["/**\n * Ready-made Rollup plugin for twee-ts.\n * Compiles .tw files and emits HTML as an asset. Compile errors fail the build\n * and emit nothing; warnings go through Rollup's warnings.\n */\nimport { readdirSync, statSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport type { CompileOptions, CompileResult, Diagnostic } from '../types.js';\nimport { compileForOutputFile } from '../compiler.js';\nimport { outputPaths, realPathOf, walkedEntry } from '../filesystem.js';\nimport type { OutputPaths } from '../filesystem.js';\nimport { fatalError, formatDiagnostic, splitDiagnostics } from './diagnostics.js';\nimport type { LocatedError } from './diagnostics.js';\nimport { createOutputRecord, outputLocations } from './paths.js';\nimport type { OutputLocation } from './paths.js';\n\nexport interface TweeTsRollupPluginOptions {\n /** Source directories/files to compile. */\n sources: string[];\n /** Story format ID. */\n format?: string;\n /** Output filename. Default: 'index.html'. */\n outputFilename?: string;\n /** Additional compile options. */\n compileOptions?: Partial<CompileOptions>;\n}\n\n/**\n * What `rollup --watch` watches for the input at `path`, whose real path is\n * `real`. `isRoot`: the input itself, which is followed wherever it leads.\n *\n * Rollup watches a folder recursively and follows the links in it. So a folder\n * is registered whole only when nothing under it is left out of the story: no\n * file or folder the build writes (`outputs`), and no link to a folder, which\n * source discovery doesn't follow and which may lead to the output. Any other\n * folder is replaced by what it contains, minus those, so writing the outputs\n * starts no build. A file added straight to such a folder is found at the next\n * build. A path the build writes to is never registered itself: Rollup fails a\n * watch build whose watched files include its `output.dir` or `output.file`.\n */\nfunction watchTargets(path: string, real: string, outputs: OutputPaths, isRoot: boolean): string[] {\n if (outputs.isFile(real) || (!isRoot && outputs.isDir(real))) return [];\n let entry;\n try {\n entry = isRoot ? { stat: statSync(path), real } : walkedEntry(path, real);\n } catch {\n // An input Rollup's watcher reports, unless it is an output location. Below an\n // input, what source discovery can't read (a link to nothing, say) is skipped.\n return isRoot && !outputs.isDir(real) ? [path] : [];\n }\n if (entry === undefined) return []; // A link to a folder, not followed.\n if (outputs.isFile(entry.real)) return [];\n if (!entry.stat.isDirectory()) return [path];\n let names: string[];\n try {\n names = readdirSync(path);\n } catch {\n return isRoot && !outputs.isDir(real) ? [path] : [];\n }\n const children = names.map((name) => {\n const child = join(path, name);\n return { path: child, targets: watchTargets(child, join(entry.real, name), outputs, false) };\n });\n const whole =\n !outputs.holds(entry.real) &&\n children.every(({ path: child, targets }) => targets.length === 1 && targets[0] === child);\n return whole ? [path] : children.flatMap(({ targets }) => targets);\n}\n\nexport function tweeTsPlugin(options: TweeTsRollupPluginOptions) {\n const outputFilename = options.outputFilename ?? 'index.html';\n // Every path this plugin's builds write: each output's folder or file, its story,\n // and the files of each bundle. A source folder may hold any of them, and none\n // may be read back as a source or rebuild under watch.\n const record = createOutputRecord(outputFilename);\n\n return {\n name: 'twee-ts',\n\n // Rollup's watch mode and CLI pass the whole config here, outputs included, so\n // they are known before the first build starts and before any is written.\n // Typed `object`: a type with only optional properties would share none with\n // Rollup's InputOptions, and the plugin would no longer be assignable to Plugin.\n options(inputOptions: object): undefined {\n const { output } = inputOptions as { readonly output?: unknown };\n for (const location of outputLocations(output)) record.addLocation(location);\n return undefined;\n },\n\n // `rollup --watch` rebuilds for the files the build registers. The story's\n // inputs are read in generateBundle, outside the module graph, so they are\n // registered here. Rollup's watcher watches a folder recursively, files added\n // to it included.\n buildStart(this: { addWatchFile: (id: string) => void; meta: { watchMode: boolean } }) {\n if (!this.meta.watchMode) return;\n const outputs = outputPaths(record.outputs());\n const extra = options.compileOptions;\n const inputs = [...options.sources, ...(extra?.headFile ? [extra.headFile] : []), ...(extra?.modules ?? [])];\n for (const input of inputs) {\n for (const target of watchTargets(resolve(input), realPathOf(input), outputs, true)) this.addWatchFile(target);\n }\n },\n\n // Runs for every output before any of them generates its bundle, when the\n // outputs are written together (as the CLI and watch mode write them).\n renderStart(outputOptions: OutputLocation): void {\n record.addLocation(outputOptions);\n },\n\n async generateBundle(\n this: {\n emitFile: (opts: { type: 'asset'; fileName: string; source: string }) => void;\n error: (error: LocatedError) => never;\n warn: (message: string) => void;\n },\n outputOptions: OutputLocation,\n bundle: Readonly<Record<string, unknown>>,\n ): Promise<void> {\n // Where this output writes the story and the bundle. A source folder may hold\n // them, or another output's, and no last build of them may be read back.\n record.addLocation(outputOptions);\n record.addFiles(outputOptions, Object.keys(bundle));\n let result: CompileResult;\n try {\n result = await compileForOutputFile(\n { sources: options.sources, formatId: options.format, ...options.compileOptions },\n record.outputs(),\n );\n } catch (e) {\n return this.error(fatalError(e));\n }\n let warnings: Diagnostic[];\n try {\n warnings = splitDiagnostics(result);\n } catch (e) {\n return this.error(e as LocatedError);\n }\n for (const w of warnings) this.warn(formatDiagnostic(w));\n this.emitFile({ type: 'asset', fileName: outputFilename, source: result.output });\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAwCA,SAAS,aAAa,MAAc,MAAc,SAAsB,QAA2B;CACjG,IAAI,QAAQ,OAAO,IAAI,KAAM,CAAC,UAAU,QAAQ,MAAM,IAAI,GAAI,OAAO,CAAC;CACtE,IAAI;CACJ,IAAI;EACF,QAAQ,SAAS;GAAE,MAAM,SAAS,IAAI;GAAG;EAAK,IAAI,YAAY,MAAM,IAAI;CAC1E,QAAQ;EAGN,OAAO,UAAU,CAAC,QAAQ,MAAM,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC;CACpD;CACA,IAAI,UAAU,KAAA,GAAW,OAAO,CAAC;CACjC,IAAI,QAAQ,OAAO,MAAM,IAAI,GAAG,OAAO,CAAC;CACxC,IAAI,CAAC,MAAM,KAAK,YAAY,GAAG,OAAO,CAAC,IAAI;CAC3C,IAAI;CACJ,IAAI;EACF,QAAQ,YAAY,IAAI;CAC1B,QAAQ;EACN,OAAO,UAAU,CAAC,QAAQ,MAAM,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC;CACpD;CACA,MAAM,WAAW,MAAM,KAAK,SAAS;EACnC,MAAM,QAAQ,KAAK,MAAM,IAAI;EAC7B,OAAO;GAAE,MAAM;GAAO,SAAS,aAAa,OAAO,KAAK,MAAM,MAAM,IAAI,GAAG,SAAS,KAAK;EAAE;CAC7F,CAAC;CAID,OAFE,CAAC,QAAQ,MAAM,MAAM,IAAI,KACzB,SAAS,OAAO,EAAE,MAAM,OAAO,cAAc,QAAQ,WAAW,KAAK,QAAQ,OAAO,KAAK,IAC5E,CAAC,IAAI,IAAI,SAAS,SAAS,EAAE,cAAc,OAAO;AACnE;AAEA,SAAgB,aAAa,SAAoC;CAC/D,MAAM,iBAAiB,QAAQ,kBAAkB;CAIjD,MAAM,SAAS,mBAAmB,cAAc;CAEhD,OAAO;EACL,MAAM;EAMN,QAAQ,cAAiC;GACvC,MAAM,EAAE,WAAW;GACnB,KAAK,MAAM,YAAY,gBAAgB,MAAM,GAAG,OAAO,YAAY,QAAQ;EAE7E;EAMA,aAAuF;GACrF,IAAI,CAAC,KAAK,KAAK,WAAW;GAC1B,MAAM,UAAU,YAAY,OAAO,QAAQ,CAAC;GAC5C,MAAM,QAAQ,QAAQ;GACtB,MAAM,SAAS;IAAC,GAAG,QAAQ;IAAS,GAAI,OAAO,WAAW,CAAC,MAAM,QAAQ,IAAI,CAAC;IAAI,GAAI,OAAO,WAAW,CAAC;GAAE;GAC3G,KAAK,MAAM,SAAS,QAClB,KAAK,MAAM,UAAU,aAAa,QAAQ,KAAK,GAAG,WAAW,KAAK,GAAG,SAAS,IAAI,GAAG,KAAK,aAAa,MAAM;EAEjH;EAIA,YAAY,eAAqC;GAC/C,OAAO,YAAY,aAAa;EAClC;EAEA,MAAM,eAMJ,eACA,QACe;GAGf,OAAO,YAAY,aAAa;GAChC,OAAO,SAAS,eAAe,OAAO,KAAK,MAAM,CAAC;GAClD,IAAI;GACJ,IAAI;IACF,SAAS,MAAM,qBACb;KAAE,SAAS,QAAQ;KAAS,UAAU,QAAQ;KAAQ,GAAG,QAAQ;IAAe,GAChF,OAAO,QAAQ,CACjB;GACF,SAAS,GAAG;IACV,OAAO,KAAK,MAAM,WAAW,CAAC,CAAC;GACjC;GACA,IAAI;GACJ,IAAI;IACF,WAAW,iBAAiB,MAAM;GACpC,SAAS,GAAG;IACV,OAAO,KAAK,MAAM,CAAiB;GACrC;GACA,KAAK,MAAM,KAAK,UAAU,KAAK,KAAK,iBAAiB,CAAC,CAAC;GACvD,KAAK,SAAS;IAAE,MAAM;IAAS,UAAU;IAAgB,QAAQ,OAAO;GAAO,CAAC;EAClF;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"rollup.js","names":[],"sources":["../../src/plugins/link-watch.ts","../../src/plugins/rollup.ts"],"sourcesContent":["/**\n * Tells a bundler's build watch that a link the story's inputs are reached through was pointed elsewhere, when the\n * bundler's own watcher can't. Rollup's watcher (chokidar) follows a registered link and watches the file it leads to,\n * so replacing the link, or a hop of a chain of links, raises nothing. The link's folder is watched here instead (not\n * recursively, so what the build writes below it is not reported), and a change of any link starts a build the way an\n * edit of a registered file does: by touching a file the plugin has registered with the bundler.\n */\nimport { mkdtempSync, readlinkSync, rmSync, utimesSync, watch, writeFileSync } from 'node:fs';\nimport type { FSWatcher } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { dirname, join } from 'node:path';\n\n/** How long a burst of file system events settles before the links are read again (a link is replaced in two steps). */\nconst SETTLE_MS = 30;\n\nexport interface LinkWatch {\n /** The file to register with the bundler's watcher: written when a link changed, with the number of changes so far. */\n readonly signal: string;\n /** Watches `links` (platform paths) from now on, instead of the links watched before. */\n watch(links: readonly string[]): void;\n /** Stops watching and removes the signal file. */\n close(): void;\n}\n\n/** Where a link leads (as written), or nothing when it is no link or does not exist. */\nfunction destination(link: string): string | undefined {\n try {\n return readlinkSync(link);\n } catch {\n return undefined;\n }\n}\n\n/**\n * A watch of links that touches its signal file when one of them leads elsewhere, or appears or disappears. The watch\n * is a refinement of the build watch: without a temporary folder to write the signal file in (a missing or read-only\n * TMPDIR), there is none, and a retargeted link is read with the next build something else starts.\n */\nexport function createLinkWatch(): LinkWatch | undefined {\n let folder: string | undefined;\n let signal: string;\n try {\n folder = mkdtempSync(join(tmpdir(), 'twee-ts-links-'));\n signal = join(folder, 'changed');\n writeFileSync(signal, '');\n } catch {\n if (folder !== undefined) rmSync(folder, { recursive: true, force: true });\n return undefined;\n }\n const signalFolder = folder;\n let touches = 0;\n const watchers: FSWatcher[] = [];\n let timer: NodeJS.Timeout | undefined;\n let known = new Map<string, string | undefined>();\n\n const touch = (): void => {\n touches += 1;\n // A modification time that differs from every earlier one, whatever the clock's resolution.\n const at = new Date(Date.now() + touches * 1000);\n try {\n writeFileSync(signal, String(touches));\n utimesSync(signal, at, at);\n } catch {\n // The watch is closed.\n }\n };\n const check = (): void => {\n timer = undefined;\n const changed = [...known].filter(([link, was]) => destination(link) !== was);\n if (changed.length === 0) return;\n known = new Map([...known.keys()].map((link) => [link, destination(link)]));\n touch();\n };\n /** Reads the links again once the events that come together have come. */\n const schedule = (): void => {\n clearTimeout(timer);\n timer = setTimeout(check, SETTLE_MS);\n };\n const stopWatching = (): void => {\n for (const watcher of watchers.splice(0)) watcher.close();\n clearTimeout(timer);\n timer = undefined;\n };\n const onExit = (): void => {\n rmSync(signalFolder, { recursive: true, force: true });\n };\n process.once('exit', onExit);\n\n return {\n signal,\n watch(links) {\n stopWatching();\n known = new Map(links.map((link) => [link, destination(link)]));\n for (const parent of new Set(links.map((link) => dirname(link)))) {\n try {\n const watcher = watch(parent, schedule);\n // A folder that goes away, or a limit of the system: the links in it are read again.\n watcher.on('error', schedule);\n watchers.push(watcher);\n } catch {\n // The folder can't be watched; the links in it are read again with the next build.\n }\n }\n },\n close() {\n stopWatching();\n process.removeListener('exit', onExit);\n onExit();\n },\n };\n}\n","/**\n * Ready-made Rollup plugin for twee-ts.\n * Compiles .tw files and emits HTML as an asset. Compile errors fail the build\n * and emit nothing; warnings go through Rollup's warnings.\n */\nimport { resolve, sep } from 'node:path';\nimport { outputPaths } from '../filesystem.js';\nimport { recordOwnOutput } from '../atomic-write.js';\nimport { compileStory, fatalError } from './diagnostics.js';\nimport type { CompiledStory, LocatedError } from './diagnostics.js';\nimport { resolvePluginOptions } from './options.js';\nimport type { SharedPluginOptions } from './options.js';\nimport { createOutputRecord, emittedFilePath, outputLocations } from './paths.js';\nimport type { OutputLocation } from './paths.js';\nimport { createLinkWatch } from './link-watch.js';\nimport type { LinkWatch } from './link-watch.js';\nimport { watchPlan } from './watch-targets.js';\nimport { identify } from '../path-identity.js';\n\nexport type { PluginCompileOptions } from './options.js';\n\nexport type TweeTsRollupPluginOptions = SharedPluginOptions;\n\nexport function tweeTsPlugin(options: TweeTsRollupPluginOptions) {\n const resolved = resolvePluginOptions('rollup', options);\n const { outputFilename } = resolved;\n // Every path this plugin's builds write: each output's folder or file, its story,\n // and the files of each bundle. A source folder may hold any of them, and none\n // may be read back as a source or rebuild under watch.\n const record = createOutputRecord(outputFilename);\n // Tells the watch that a link the inputs are reached through was pointed elsewhere (see link-watch.ts).\n let linkWatch: LinkWatch | undefined;\n // The link watch could not be set up (no temporary folder); said once, and not tried again.\n let linkWatchUnavailable = false;\n // Where each registered path led when Rollup's watcher first followed it.\n const followed = new Map<string, string>();\n\n return {\n name: 'twee-ts',\n\n // Rollup's watch mode and CLI pass the whole config here, outputs included, so\n // they are known before the first build starts and before any is written.\n // Typed `object`: a type with only optional properties would share none with\n // Rollup's InputOptions, and the plugin would no longer be assignable to Plugin.\n options(inputOptions: object): undefined {\n const output: unknown = Reflect.get(inputOptions, 'output');\n for (const location of outputLocations(output)) record.addLocation(location);\n return undefined;\n },\n\n // `rollup --watch` rebuilds for the files the build registers. The story's\n // inputs are read in generateBundle, outside the module graph, so they are\n // registered here: the sources, the head file and the modules, without the\n // files `exclude` leaves out (see watchTargets). Rollup's watcher follows a link and watches what it leads to, so\n // it doesn't see a link, or a hop of a chain of links, pointed elsewhere; the plugin watches the links itself.\n buildStart(this: {\n addWatchFile: (id: string) => void;\n warn: (message: string) => void;\n meta: { watchMode: boolean };\n }) {\n if (!this.meta.watchMode) return;\n const outputs = outputPaths(record.outputs(), 'snapshot');\n // In the platform's own form, as Rollup reports its module ids.\n const plan = watchPlan(resolved.inputs, resolved.excluded, outputs, 'given');\n const targets = plan.targets.map((target) => identify(target).absolute);\n for (const target of targets) this.addWatchFile(target);\n // Rollup's watcher keeps watching what a path led to when it was registered, so a path that leads elsewhere now\n // (a link was pointed at another file or folder) is watched from its new location as well.\n for (const [path, real] of plan.reals) {\n const first = followed.get(path) ?? real;\n followed.set(path, first);\n if (first !== real) this.addWatchFile(resolve(real));\n }\n if (plan.links.length > 0 && !linkWatchUnavailable) {\n linkWatch ??= createLinkWatch();\n if (linkWatch === undefined) {\n // The links are a refinement: the build goes on, and says once what the watch cannot do.\n linkWatchUnavailable = true;\n this.warn(\n '[twee-ts] The link watch is unavailable because no temporary folder can be written (check TMPDIR); ' +\n 'a link pointed at another target is read with the next build something else starts.',\n );\n } else {\n linkWatch.watch(plan.links);\n this.addWatchFile(linkWatch.signal);\n }\n }\n // Rollup's file watcher (chokidar) reads every backslash as a separator, also on POSIX, where it is a\n // filename character: such a file is read by each build, but an edit to it starts none.\n const unwatchable = sep === '/' ? targets.filter((target) => target.includes('\\\\')) : [];\n if (unwatchable.length > 0) {\n this.warn(\n `[twee-ts] Rollup's watch mode cannot watch ${unwatchable.map((target) => JSON.stringify(target)).join(', ')} ` +\n 'because the name contains a backslash; edits to it will not rebuild the story.',\n );\n }\n },\n\n // The watcher is closed: the links are no longer watched.\n closeWatcher(): void {\n linkWatch?.close();\n linkWatch = undefined;\n },\n\n // Runs for every output before any of them generates its bundle, when the\n // outputs are written together (as the CLI and watch mode write them).\n renderStart(outputOptions: OutputLocation): void {\n record.addLocation(outputOptions);\n },\n\n async generateBundle(\n this: {\n emitFile: (opts: { type: 'asset'; fileName: string; source: string }) => void;\n error: (error: LocatedError | string) => never;\n warn: (message: string) => void;\n },\n outputOptions: OutputLocation,\n bundle: Readonly<Record<string, unknown>>,\n ): Promise<void> {\n if (Object.hasOwn(bundle, outputFilename)) {\n return this.error(\n `twee-ts: the bundle already holds a file named ${outputFilename}; set the plugin's outputFilename to another name.`,\n );\n }\n // Where this output writes the story and the bundle. A source folder may hold\n // them, or another output's, and no last build of them may be read back.\n record.addLocation(outputOptions);\n record.addFiles(outputOptions, Object.keys(bundle));\n let story: CompiledStory;\n try {\n story = await compileStory(resolved.compile(), record.outputs(), undefined, true);\n } catch (e) {\n return this.error(fatalError(e));\n }\n for (const warning of story.warnings) this.warn(warning);\n this.emitFile({ type: 'asset', fileName: outputFilename, source: story.output });\n },\n\n writeBundle(\n outputOptions: OutputLocation,\n bundle: Readonly<\n Record<string, { readonly type: 'asset'; readonly source: string | Uint8Array } | { readonly type: 'chunk' }>\n >,\n ): void {\n const asset = bundle[outputFilename];\n const destination = emittedFilePath(outputOptions, outputFilename);\n // Rollup writes the asset itself. Only a completed write establishes ownership, including for output\n // modes without creator markers; generate() must leave the ownership of existing disk bytes unchanged.\n if (asset?.type === 'asset' && destination !== undefined) recordOwnOutput(destination, asset.source);\n },\n };\n}\n"],"mappings":"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"}
|