-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathtsup.config.ts
More file actions
119 lines (108 loc) · 3.65 KB
/
Copy pathtsup.config.ts
File metadata and controls
119 lines (108 loc) · 3.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { defineConfig } from "tsup";
import type { Plugin } from "esbuild";
import path from "path";
import { validateManifest } from "./src/build/validate-manifest";
validateManifest();
/**
* Esbuild plugin that bundles `.inline.ts` files into browser-ready JavaScript strings.
*
* Problem: Inline scripts are embedded as raw text into `<script>` tags in the browser.
* The previous text-loader read `.inline.ts` files verbatim, meaning TypeScript syntax
* (type annotations, `as` casts, non-null assertions, interfaces) survived into the
* browser and caused parse errors.
*
* Solution: Use `esbuild.build()` to transpile TypeScript and bundle local/npm imports
* into a single self-contained script. The result is returned as a text string that can
* be safely injected into a `<script>` tag.
*
* This mirrors Quartz v5's own `inline-script-loader` in `quartz/cli/handlers.js`.
*/
const inlineScriptPlugin: Plugin = {
name: "inline-script-loader",
setup(parentBuild) {
const absWorkingDir = parentBuild.initialOptions.absWorkingDir ?? process.cwd();
// SCSS files are compiled to CSS via sass, matching Quartz v5 core behavior
parentBuild.onLoad({ filter: /\.scss$/ }, async (args) => {
const sass = await import("sass");
const result = sass.compile(args.path);
return { contents: result.css, loader: "text" };
});
// Inline TypeScript files are transpiled + bundled for the browser
parentBuild.onLoad({ filter: /\.inline\.ts$/ }, async (args) => {
const esbuild = await import("esbuild");
const fs = await import("fs");
let text = await fs.promises.readFile(args.path, "utf8");
// Strip export statements that were added for the module system —
// inline scripts run in a <script> tag, not as ES modules
text = text.replace(/^export default /gm, "");
text = text.replace(/^export /gm, "");
const resolveDir = path.dirname(args.path);
const sourcefile = path.relative(absWorkingDir, args.path);
const result = await esbuild.build({
stdin: {
contents: text,
loader: "ts",
resolveDir,
sourcefile,
},
write: false,
bundle: true,
minify: true,
platform: "browser",
format: "esm",
target: "es2020",
sourcemap: false,
// Preserve dynamic CDN imports (e.g. graph plugin loading d3/pixi from CDN)
external: ["http://*", "https://*"],
});
const js = result.outputFiles?.[0]?.text;
if (!js) throw new Error(`inline-script-loader: no JS output for ${args.path}`);
return {
contents: js,
loader: "text",
};
});
},
};
/**
* Singleton externals: packages that MUST be the same instance at runtime
* across all plugins and the Quartz host. Everything else gets bundled.
*/
const SINGLETON_EXTERNALS = [
"preact",
"preact/hooks",
"preact/jsx-runtime",
"preact/compat",
"@jackyzha0/quartz",
"@jackyzha0/quartz/*",
"vfile",
"vfile/*",
"unified",
];
export default defineConfig({
entry: {
index: "src/index.ts",
types: "src/types.ts",
"components/index": "src/components/index.ts",
},
format: ["esm"],
dts: true,
tsconfig: "tsconfig.build.json",
sourcemap: true,
clean: true,
treeshake: true,
target: "es2022",
splitting: false,
outDir: "dist",
platform: "node",
noExternal: [/.*/],
external: SINGLETON_EXTERNALS,
banner: {
js: 'import { createRequire } from "module"; const require = createRequire(import.meta.url);',
},
esbuildOptions(options) {
options.jsx = "automatic";
options.jsxImportSource = "preact";
},
esbuildPlugins: [inlineScriptPlugin],
});