@torpor/unplugin 1.0.5 → 1.2.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/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +130 -12
- package/dist/index.mjs.map +1 -1
- package/dist/types.d.mts +9 -1
- package/dist/types.d.mts.map +1 -1
- package/package.json +8 -4
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;cAWa,iBAAiB,gBAAgB;cA+TjC,UAAU,iBAAiB"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
1
2
|
import { build, parse } from "@torpor/view/compile";
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
2
5
|
import { createUnplugin } from "unplugin";
|
|
3
6
|
import { transformWithOxc } from "vite";
|
|
4
7
|
//#region src/index.ts
|
|
@@ -12,22 +15,33 @@ const unpluginFactory = (options) => ({
|
|
|
12
15
|
if (styles.has(id)) return styles.get(id);
|
|
13
16
|
},
|
|
14
17
|
transformInclude(id) {
|
|
15
|
-
|
|
18
|
+
if (/\.torp([?#]|$)/.test(id)) return true;
|
|
19
|
+
const query = getQuery(id);
|
|
20
|
+
if (query?.has("client") || query?.has("server")) return /\.(js|mjs|cjs|ts|mts|cts|jsx|tsx)$/.test(cleanId(id));
|
|
21
|
+
return false;
|
|
16
22
|
},
|
|
17
23
|
transform(code, id, viteOptions) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
let transformOptions = { ...options };
|
|
25
|
+
if (viteOptions && viteOptions.dev !== void 0) transformOptions.dev = viteOptions.dev;
|
|
26
|
+
const override = getOverride(getQuery(id));
|
|
27
|
+
if (override === "client") transformOptions.server = false;
|
|
28
|
+
else if (override === "server") transformOptions.server = true;
|
|
29
|
+
else {
|
|
30
|
+
if (viteOptions && viteOptions.ssr !== void 0) transformOptions.server = viteOptions.ssr;
|
|
31
|
+
if (transformOptions.test) transformOptions.server = true;
|
|
21
32
|
}
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
|
|
33
|
+
if (!/\.torp([?#]|$)/.test(id)) {
|
|
34
|
+
if (!transformOptions.test || !override) return;
|
|
35
|
+
const rewritten = propagateOverride(code, override, path.dirname(cleanId(id)));
|
|
36
|
+
return rewritten === code ? void 0 : {
|
|
37
|
+
code: rewritten,
|
|
38
|
+
map: null
|
|
39
|
+
};
|
|
25
40
|
}
|
|
26
|
-
if (options?.test) options.server = true;
|
|
27
41
|
let parsed = parse(code);
|
|
28
|
-
if (parsed.ok && parsed.template) return transform(parsed.template, id,
|
|
42
|
+
if (parsed.ok && parsed.template) return transform(parsed.template, id, transformOptions, override);
|
|
29
43
|
else {
|
|
30
|
-
let name = id.split(/[\\/]/).at(-1).replace(/\.torp
|
|
44
|
+
let name = id.split(/[\\/]/).at(-1).replace(/\.torp.*$/, "");
|
|
31
45
|
let errorMessages = parsed.errors.map((e) => `${e.startLine + 1},${e.startChar}: ${e.message}`);
|
|
32
46
|
console.log(`\nERRORS: ${id}\n======\n${errorMessages.join("\n")}`);
|
|
33
47
|
let errorCode = `
|
|
@@ -44,20 +58,124 @@ export default function Error() {
|
|
|
44
58
|
}
|
|
45
59
|
}`;
|
|
46
60
|
let errorParsed = parse(errorCode);
|
|
47
|
-
if (errorParsed.ok && errorParsed.template) return transform(errorParsed.template, id,
|
|
61
|
+
if (errorParsed.ok && errorParsed.template) return transform(errorParsed.template, id, transformOptions, override);
|
|
48
62
|
throw new Error(`Parse failed for ${id}, ${errorMessages.join("\n")}`);
|
|
49
63
|
}
|
|
50
64
|
}
|
|
51
65
|
});
|
|
52
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Gets the query string of a module id (e.g. `client` for `Foo.torp?client`)
|
|
68
|
+
*/
|
|
69
|
+
function getQuery(id) {
|
|
70
|
+
const queryStart = id.lastIndexOf("?");
|
|
71
|
+
if (queryStart === -1) return;
|
|
72
|
+
return new URLSearchParams(id.substring(queryStart + 1));
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Gets the client/server override from a module id's query, if any
|
|
76
|
+
*/
|
|
77
|
+
function getOverride(query) {
|
|
78
|
+
if (query?.has("client")) return "client";
|
|
79
|
+
if (query?.has("server")) return "server";
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Gets the id without its query string (e.g. `Foo.torp` for `Foo.torp?client`)
|
|
83
|
+
*/
|
|
84
|
+
function cleanId(id) {
|
|
85
|
+
return id.replace(/[?#].*$/, "");
|
|
86
|
+
}
|
|
87
|
+
function transform(template, id, options, override) {
|
|
53
88
|
const built = build(template, options);
|
|
54
89
|
let transformed = built.code;
|
|
90
|
+
if (options?.test && override) transformed = propagateOverride(transformed, override, path.dirname(cleanId(id)));
|
|
55
91
|
if (built.styles) for (let style of built.styles) {
|
|
56
92
|
transformed = `import '${style.hash}.css';\n` + transformed;
|
|
57
93
|
styles.set(style.hash + ".css", style.style);
|
|
58
94
|
}
|
|
59
95
|
return transformWithOxc(transformed, id.replace(/\.torp.*$/, ".ts"));
|
|
60
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Rewrites a component's imports so that the components it imports get the
|
|
99
|
+
* same `?client`/`?server` override:
|
|
100
|
+
*
|
|
101
|
+
* - relative `.torp` imports get the query appended
|
|
102
|
+
* - bare package imports that resolve into a torpor package (one that ships
|
|
103
|
+
* `.torp` files, e.g. `@torpor/ui/Progress` or `phosphor-torpor/lib/Camera`)
|
|
104
|
+
* get the query appended too -- their specifiers don't end in `.torp`, but
|
|
105
|
+
* they resolve to `.torp` files (directly or through plain-JS re-export
|
|
106
|
+
* barrels), so without the query they'd be compiled for the default side
|
|
107
|
+
*/
|
|
108
|
+
function propagateOverride(code, override, importerDir) {
|
|
109
|
+
let result = code;
|
|
110
|
+
result = result.replace(/(from\s*['"])([^'"]+\.torp)(['"])/g, `$1$2?${override}$3`);
|
|
111
|
+
result = result.replace(/(from\s*['"])([^'".#/][^'"]*)(['"])/g, (match, pre, specifier, post) => {
|
|
112
|
+
if (specifier.includes("?")) return match;
|
|
113
|
+
if (!isTorporPackageImport(specifier, importerDir)) return match;
|
|
114
|
+
return `${pre}${specifier}?${override}${post}`;
|
|
115
|
+
});
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Cache of whether a package (per importing directory) is a torpor package
|
|
120
|
+
*/
|
|
121
|
+
const torporPackages = /* @__PURE__ */ new Map();
|
|
122
|
+
/**
|
|
123
|
+
* Whether a bare import specifier (e.g. `@torpor/ui/Progress`) resolves into a
|
|
124
|
+
* package that ships `.torp` files, and so needs override queries passed on
|
|
125
|
+
* to it
|
|
126
|
+
*/
|
|
127
|
+
function isTorporPackageImport(specifier, importerDir) {
|
|
128
|
+
const pkgName = /^(@[^/]+\/[^/]+|[^@./][^/]*)/.exec(specifier)?.[0];
|
|
129
|
+
if (!pkgName) return false;
|
|
130
|
+
const cacheKey = `${importerDir}\0${pkgName}`;
|
|
131
|
+
let isTorpor = torporPackages.get(cacheKey);
|
|
132
|
+
if (isTorpor === void 0) {
|
|
133
|
+
isTorpor = packageShipsTorp(pkgName, importerDir);
|
|
134
|
+
torporPackages.set(cacheKey, isTorpor);
|
|
135
|
+
}
|
|
136
|
+
return isTorpor;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Whether a package is a torpor package: it declares a `torpor` entry in its
|
|
140
|
+
* package.json (usually `true`, or a path mirroring the `svelte` field
|
|
141
|
+
* convention), or any of its export targets is a `.torp` file
|
|
142
|
+
*/
|
|
143
|
+
function packageShipsTorp(pkgName, importerDir) {
|
|
144
|
+
const packageJsonPath = findPackageJson(pkgName, importerDir);
|
|
145
|
+
if (!packageJsonPath) return false;
|
|
146
|
+
try {
|
|
147
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
148
|
+
if (pkg.torpor === true || typeof pkg.torpor === "string") return true;
|
|
149
|
+
return exportsIncludeTorp(pkg.exports);
|
|
150
|
+
} catch {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Finds a package's package.json, either by resolving it through the
|
|
156
|
+
* package's own exports map, or (when the exports map doesn't expose
|
|
157
|
+
* `./package.json`) through a node_modules walk-up from the importing file
|
|
158
|
+
*/
|
|
159
|
+
function findPackageJson(pkgName, importerDir) {
|
|
160
|
+
try {
|
|
161
|
+
const packageJson = createRequire(path.join(importerDir, "index.js")).resolve(`${pkgName}/package.json`);
|
|
162
|
+
if (path.isAbsolute(packageJson)) return packageJson;
|
|
163
|
+
} catch {}
|
|
164
|
+
let dir = importerDir;
|
|
165
|
+
while (true) {
|
|
166
|
+
const packageJson = path.join(dir, "node_modules", ...pkgName.split("/"), "package.json");
|
|
167
|
+
if (existsSync(packageJson)) return packageJson;
|
|
168
|
+
const parent = path.dirname(dir);
|
|
169
|
+
if (parent === dir) return;
|
|
170
|
+
dir = parent;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function exportsIncludeTorp(exports) {
|
|
174
|
+
if (typeof exports === "string") return exports.endsWith(".torp");
|
|
175
|
+
if (Array.isArray(exports)) return exports.some(exportsIncludeTorp);
|
|
176
|
+
if (exports && typeof exports === "object") return Object.values(exports).some(exportsIncludeTorp);
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
61
179
|
const unplugin = /* #__PURE__ */ createUnplugin(unpluginFactory);
|
|
62
180
|
//#endregion
|
|
63
181
|
export { unplugin as default, unplugin, unpluginFactory };
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { type Template, build, parse } from \"@torpor/view/compile\";\nimport { type UnpluginFactory, type UnpluginInstance } from \"unplugin\";\nimport { createUnplugin } from \"unplugin\";\nimport { transformWithOxc } from \"vite\";\nimport type Options from \"./types\";\n\nconst styles = new Map<string, string>();\n\nexport const unpluginFactory: UnpluginFactory<Options | undefined> = (options) => ({\n\tname: \"unplugin-torpor\",\n\tresolveId(id /*, importer, options*/) {\n\t\tif (styles.has(id)) {\n\t\t\treturn id;\n\t\t}\n\t\treturn undefined;\n\t},\n\tload(id) {\n\t\tif (styles.has(id)) {\n\t\t\treturn styles.get(id);\n\t\t}\n\t\treturn undefined;\n\t},\n\ttransformInclude(id) {\n\t\t// Check for *.torp files\n\t\treturn /\\.torp\\?*/.test(id);\n\t},\n\t// @ts-ignore\n\ttransform(code, id, viteOptions) {\n\t\t// We may be in dev mode\n\t\tif (viteOptions && viteOptions.dev !== undefined) {\n\t\t\toptions ??= {};\n\t\t\toptions.dev = viteOptions.dev;\n\t\t}\n\n\t\t// Vite can override user server options\n\t\tif (viteOptions && viteOptions.ssr !== undefined) {\n\t\t\toptions ??= {};\n\t\t\toptions.server = viteOptions.ssr;\n\t\t}\n\n\t\t// But when testing we always generate for the server\n\t\tif (options?.test) {\n\t\t\toptions.server = true;\n\t\t}\n\n\t\t// Try to parse the code\n\t\tlet parsed = parse(code);\n\t\tif (parsed.ok && parsed.template) {\n\t\t\t// Transform for server or client\n\t\t\treturn transform(parsed.template, id, options);\n\t\t} else {\n\t\t\t// Show an error component\n\t\t\tlet name = id\n\t\t\t\t.split(/[\\\\/]/)\n\t\t\t\t.at(-1)\n\t\t\t\t.replace(/\\.torp$/, \"\")!;\n\t\t\tlet errorMessages = parsed.errors.map(\n\t\t\t\t(e) => `${e.startLine + 1},${e.startChar}: ${e.message}`,\n\t\t\t);\n\t\t\tconsole.log(`\\nERRORS: ${id}\\n======\\n${errorMessages.join(\"\\n\")}`);\n\t\t\tlet errorCode = `\nexport default function Error() {\n\t@render {\n\t\t<div style=\"background-color: #222; color: #f44\"; font-size: 15px; line-height: 1.5;\">\n\t\t\t<p style=\"margin: 0; padding: 0;\">\n\t\t\t\t<strong>Error${parsed.errors.length === 1 ? \"\" : \"s\"} in ${name}:</strong>\n\t\t\t</p>\n\t\t\t<ul style=\"margin: 0; padding: 0 20px\">\n\t\t\t\t${errorMessages.map((e) => `<li>${e}</li>`).join(\"\\n\")}\n\t\t\t</ul>\n\t\t</div>\n\t}\n}`;\n\t\t\tlet errorParsed = parse(errorCode);\n\t\t\tif (errorParsed.ok && errorParsed.template) {\n\t\t\t\treturn transform(errorParsed.template, id, options);\n\t\t\t}\n\t\t\t// This should never be reached, but just in case...\n\t\t\tthrow new Error(`Parse failed for ${id}, ${errorMessages.join(\"\\n\")}`);\n\t\t}\n\t},\n});\n\nfunction transform(template: Template, id: string, options?: Options) {\n\tconst built = build(template, options);\n\tlet transformed = built.code;\n\n\tif (built.styles) {\n\t\tfor (let style of built.styles) {\n\t\t\t// Add a dynamic import for the component's CSS with a name from\n\t\t\t// the hash and add the styles to a map. Then resolveId will\n\t\t\t// pass the CSS id onto load, which will load the the actual CSS\n\t\t\t// from the map\n\t\t\ttransformed = `import '${style.hash}.css';\\n` + transformed;\n\t\t\tstyles.set(style.hash + \".css\", style.style);\n\t\t}\n\t}\n\n\t//printTransformed(transformed);\n\n\t// TODO: Compile typescript only if script lang=\"ts\" or config.lang=\"ts\"\n\treturn transformWithOxc(transformed, id.replace(/\\.torp.*$/, \".ts\"));\n}\n\n/*\nfunction printTransformed(transformed: string) {\n\tconsole.log(\n\t\ttransformed\n\t\t\t.split(\"\\n\")\n\t\t\t.map((l, i) => `${(i + 1).toString().padEnd(3)} ${l}`)\n\t\t\t.join(\"\\n\"),\n\t);\n}\n*/\n\nexport const unplugin: UnpluginInstance<Options | undefined, boolean> =\n\t/* #__PURE__ */ createUnplugin(unpluginFactory);\n\nexport default unplugin;\n"],"mappings":";;;;AAMA,MAAM,yBAAS,IAAI,IAAoB;AAEvC,MAAa,mBAAyD,aAAa;CAClF,MAAM;CACN,UAAU,IAA4B;EACrC,IAAI,OAAO,IAAI,EAAE,GAChB,OAAO;CAGT;CACA,KAAK,IAAI;EACR,IAAI,OAAO,IAAI,EAAE,GAChB,OAAO,OAAO,IAAI,EAAE;CAGtB;CACA,iBAAiB,IAAI;EAEpB,OAAO,YAAY,KAAK,EAAE;CAC3B;CAEA,UAAU,MAAM,IAAI,aAAa;EAEhC,IAAI,eAAe,YAAY,QAAQ,KAAA,GAAW;GACjD,YAAY,CAAC;GACb,QAAQ,MAAM,YAAY;EAC3B;EAGA,IAAI,eAAe,YAAY,QAAQ,KAAA,GAAW;GACjD,YAAY,CAAC;GACb,QAAQ,SAAS,YAAY;EAC9B;EAGA,IAAI,SAAS,MACZ,QAAQ,SAAS;EAIlB,IAAI,SAAS,MAAM,IAAI;EACvB,IAAI,OAAO,MAAM,OAAO,UAEvB,OAAO,UAAU,OAAO,UAAU,IAAI,OAAO;OACvC;GAEN,IAAI,OAAO,GACT,MAAM,OAAO,CAAC,CACd,GAAG,EAAE,CAAC,CACN,QAAQ,WAAW,EAAE;GACvB,IAAI,gBAAgB,OAAO,OAAO,KAChC,MAAM,GAAG,EAAE,YAAY,EAAE,GAAG,EAAE,UAAU,IAAI,EAAE,SAChD;GACA,QAAQ,IAAI,aAAa,GAAG,YAAY,cAAc,KAAK,IAAI,GAAG;GAClE,IAAI,YAAY;;;;;mBAKA,OAAO,OAAO,WAAW,IAAI,KAAK,IAAI,MAAM,KAAK;;;MAG9D,cAAc,KAAK,MAAM,OAAO,EAAE,MAAM,CAAC,CAAC,KAAK,IAAI,EAAE;;;;;GAKxD,IAAI,cAAc,MAAM,SAAS;GACjC,IAAI,YAAY,MAAM,YAAY,UACjC,OAAO,UAAU,YAAY,UAAU,IAAI,OAAO;GAGnD,MAAM,IAAI,MAAM,oBAAoB,GAAG,IAAI,cAAc,KAAK,IAAI,GAAG;EACtE;CACD;AACD;AAEA,SAAS,UAAU,UAAoB,IAAY,SAAmB;CACrE,MAAM,QAAQ,MAAM,UAAU,OAAO;CACrC,IAAI,cAAc,MAAM;CAExB,IAAI,MAAM,QACT,KAAK,IAAI,SAAS,MAAM,QAAQ;EAK/B,cAAc,WAAW,MAAM,KAAK,YAAY;EAChD,OAAO,IAAI,MAAM,OAAO,QAAQ,MAAM,KAAK;CAC5C;CAMD,OAAO,iBAAiB,aAAa,GAAG,QAAQ,aAAa,KAAK,CAAC;AACpE;AAaA,MAAa,WACI,+BAAe,eAAe"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { type Template, build, parse } from \"@torpor/view/compile\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\nimport { type UnpluginFactory, type UnpluginInstance } from \"unplugin\";\nimport { createUnplugin } from \"unplugin\";\nimport { transformWithOxc } from \"vite\";\nimport type Options from \"./types\";\n\nconst styles = new Map<string, string>();\n\nexport const unpluginFactory: UnpluginFactory<Options | undefined> = (options) => ({\n\tname: \"unplugin-torpor\",\n\tresolveId(id /*, importer, options*/) {\n\t\tif (styles.has(id)) {\n\t\t\treturn id;\n\t\t}\n\t\treturn undefined;\n\t},\n\tload(id) {\n\t\tif (styles.has(id)) {\n\t\t\treturn styles.get(id);\n\t\t}\n\t\treturn undefined;\n\t},\n\ttransformInclude(id) {\n\t\t// Check for *.torp files (with or without a query)\n\t\tif (/\\.torp([?#]|$)/.test(id)) {\n\t\t\treturn true;\n\t\t}\n\t\t// Also plain-JS modules that are imported with an override query --\n\t\t// re-export barrels from torpor packages (e.g. the `index.js` files\n\t\t// that `@torpor/ui/*` resolves to), which need to pass the query on\n\t\t// to the `.torp` files they re-export\n\t\tconst query = getQuery(id);\n\t\tif (query?.has(\"client\") || query?.has(\"server\")) {\n\t\t\treturn /\\.(js|mjs|cjs|ts|mts|cts|jsx|tsx)$/.test(cleanId(id));\n\t\t}\n\t\treturn false;\n\t},\n\t// @ts-ignore\n\ttransform(code, id, viteOptions) {\n\t\t// Copy the factory options instead of mutating them, so that the\n\t\t// per-request dev/server overrides below don't stick around and\n\t\t// surprise the next request\n\t\tlet transformOptions: Options = { ...options };\n\n\t\t// We may be in dev mode\n\t\tif (viteOptions && viteOptions.dev !== undefined) {\n\t\t\ttransformOptions.dev = viteOptions.dev;\n\t\t}\n\n\t\t// An explicit ?client or ?server query on the import overrides\n\t\t// everything else -- e.g. importing `Component.torp?client` in a test\n\t\t// run compiles the component for the client, so that it can be\n\t\t// mounted, while other components stay SSR-compiled\n\t\tconst override = getOverride(getQuery(id));\n\t\tif (override === \"client\") {\n\t\t\ttransformOptions.server = false;\n\t\t} else if (override === \"server\") {\n\t\t\ttransformOptions.server = true;\n\t\t} else {\n\t\t\t// Vite can override user server options\n\t\t\tif (viteOptions && viteOptions.ssr !== undefined) {\n\t\t\t\ttransformOptions.server = viteOptions.ssr;\n\t\t\t}\n\n\t\t\t// But when testing we always generate for the server\n\t\t\tif (transformOptions.test) {\n\t\t\t\ttransformOptions.server = true;\n\t\t\t}\n\t\t}\n\n\t\t// A plain-JS module with an override query (a re-export barrel from a\n\t\t// torpor package) doesn't get compiled as a component; it only needs\n\t\t// the query passed on to the `.torp` files it imports\n\t\tif (!/\\.torp([?#]|$)/.test(id)) {\n\t\t\tif (!transformOptions.test || !override) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tconst rewritten = propagateOverride(code, override, path.dirname(cleanId(id)));\n\t\t\treturn rewritten === code ? undefined : { code: rewritten, map: null };\n\t\t}\n\n\t\t// Try to parse the code\n\t\tlet parsed = parse(code);\n\t\tif (parsed.ok && parsed.template) {\n\t\t\t// Transform for server or client\n\t\t\treturn transform(parsed.template, id, transformOptions, override);\n\t\t} else {\n\t\t\t// Show an error component\n\t\t\tlet name = id\n\t\t\t\t.split(/[\\\\/]/)\n\t\t\t\t.at(-1)\n\t\t\t\t.replace(/\\.torp.*$/, \"\")!;\n\t\t\tlet errorMessages = parsed.errors.map(\n\t\t\t\t(e) => `${e.startLine + 1},${e.startChar}: ${e.message}`,\n\t\t\t);\n\t\t\tconsole.log(`\\nERRORS: ${id}\\n======\\n${errorMessages.join(\"\\n\")}`);\n\t\t\tlet errorCode = `\nexport default function Error() {\n\t@render {\n\t\t<div style=\"background-color: #222; color: #f44\"; font-size: 15px; line-height: 1.5;\">\n\t\t\t<p style=\"margin: 0; padding: 0;\">\n\t\t\t\t<strong>Error${parsed.errors.length === 1 ? \"\" : \"s\"} in ${name}:</strong>\n\t\t\t</p>\n\t\t\t<ul style=\"margin: 0; padding: 0 20px\">\n\t\t\t\t${errorMessages.map((e) => `<li>${e}</li>`).join(\"\\n\")}\n\t\t\t</ul>\n\t\t</div>\n\t}\n}`;\n\t\t\tlet errorParsed = parse(errorCode);\n\t\t\tif (errorParsed.ok && errorParsed.template) {\n\t\t\t\treturn transform(errorParsed.template, id, transformOptions, override);\n\t\t\t}\n\t\t\t// This should never be reached, but just in case...\n\t\t\tthrow new Error(`Parse failed for ${id}, ${errorMessages.join(\"\\n\")}`);\n\t\t}\n\t},\n});\n\n/**\n * Gets the query string of a module id (e.g. `client` for `Foo.torp?client`)\n */\nfunction getQuery(id: string): URLSearchParams | undefined {\n\tconst queryStart = id.lastIndexOf(\"?\");\n\tif (queryStart === -1) {\n\t\treturn undefined;\n\t}\n\treturn new URLSearchParams(id.substring(queryStart + 1));\n}\n\n/**\n * Gets the client/server override from a module id's query, if any\n */\nfunction getOverride(query: URLSearchParams | undefined): \"client\" | \"server\" | undefined {\n\tif (query?.has(\"client\")) {\n\t\treturn \"client\";\n\t}\n\tif (query?.has(\"server\")) {\n\t\treturn \"server\";\n\t}\n\treturn undefined;\n}\n\n/**\n * Gets the id without its query string (e.g. `Foo.torp` for `Foo.torp?client`)\n */\nfunction cleanId(id: string): string {\n\treturn id.replace(/[?#].*$/, \"\");\n}\n\nfunction transform(\n\ttemplate: Template,\n\tid: string,\n\toptions?: Options,\n\toverride?: \"client\" | \"server\",\n) {\n\tconst built = build(template, options);\n\tlet transformed = built.code;\n\n\t// When a component is compiled with a ?client/?server override in a test\n\t// run, any child components it imports must be compiled the same way --\n\t// otherwise mounting the parent would try to render SSR children (which\n\t// don't show anything). Pass the override on to imported components:\n\t// relative `.torp` imports, and bare imports into packages that ship\n\t// `.torp` files (e.g. `@torpor/ui/*` or `phosphor-torpor/*`), whose\n\t// re-export barrels would otherwise be compiled for the default side\n\tif (options?.test && override) {\n\t\ttransformed = propagateOverride(transformed, override, path.dirname(cleanId(id)));\n\t}\n\n\tif (built.styles) {\n\t\tfor (let style of built.styles) {\n\t\t\t// Add a dynamic import for the component's CSS with a name from\n\t\t\t// the hash and add the styles to a map. Then resolveId will\n\t\t\t// pass the CSS id onto load, which will load the the actual CSS\n\t\t\t// from the map\n\t\t\ttransformed = `import '${style.hash}.css';\\n` + transformed;\n\t\t\tstyles.set(style.hash + \".css\", style.style);\n\t\t}\n\t}\n\n\t//printTransformed(transformed);\n\n\t// TODO: Compile typescript only if script lang=\"ts\" or config.lang=\"ts\"\n\treturn transformWithOxc(transformed, id.replace(/\\.torp.*$/, \".ts\"));\n}\n\n/**\n * Rewrites a component's imports so that the components it imports get the\n * same `?client`/`?server` override:\n *\n * - relative `.torp` imports get the query appended\n * - bare package imports that resolve into a torpor package (one that ships\n * `.torp` files, e.g. `@torpor/ui/Progress` or `phosphor-torpor/lib/Camera`)\n * get the query appended too -- their specifiers don't end in `.torp`, but\n * they resolve to `.torp` files (directly or through plain-JS re-export\n * barrels), so without the query they'd be compiled for the default side\n */\nfunction propagateOverride(\n\tcode: string,\n\toverride: \"client\" | \"server\",\n\timporterDir: string,\n): string {\n\tlet result = code;\n\n\t// Relative (or bare-but-.torp-suffixed) imports without an existing query\n\tresult = result.replace(/(from\\s*['\"])([^'\"]+\\.torp)(['\"])/g, `$1$2?${override}$3`);\n\n\t// Bare package imports (e.g. `@torpor/ui/Progress`)\n\tresult = result.replace(\n\t\t/(from\\s*['\"])([^'\".#/][^'\"]*)(['\"])/g,\n\t\t(match: string, pre: string, specifier: string, post: string) => {\n\t\t\tif (specifier.includes(\"?\")) {\n\t\t\t\treturn match;\n\t\t\t}\n\t\t\tif (!isTorporPackageImport(specifier, importerDir)) {\n\t\t\t\treturn match;\n\t\t\t}\n\t\t\treturn `${pre}${specifier}?${override}${post}`;\n\t\t},\n\t);\n\n\treturn result;\n}\n\n/**\n * Cache of whether a package (per importing directory) is a torpor package\n */\nconst torporPackages = new Map<string, boolean>();\n\n/**\n * Whether a bare import specifier (e.g. `@torpor/ui/Progress`) resolves into a\n * package that ships `.torp` files, and so needs override queries passed on\n * to it\n */\nfunction isTorporPackageImport(specifier: string, importerDir: string): boolean {\n\t// The package name, without any subpath (`pkg` or `@scope/pkg`)\n\tconst pkgName = /^(@[^/]+\\/[^/]+|[^@./][^/]*)/.exec(specifier)?.[0];\n\tif (!pkgName) {\n\t\treturn false;\n\t}\n\n\tconst cacheKey = `${importerDir}\\0${pkgName}`;\n\tlet isTorpor = torporPackages.get(cacheKey);\n\tif (isTorpor === undefined) {\n\t\tisTorpor = packageShipsTorp(pkgName, importerDir);\n\t\ttorporPackages.set(cacheKey, isTorpor);\n\t}\n\treturn isTorpor;\n}\n\n/**\n * Whether a package is a torpor package: it declares a `torpor` entry in its\n * package.json (usually `true`, or a path mirroring the `svelte` field\n * convention), or any of its export targets is a `.torp` file\n */\nfunction packageShipsTorp(pkgName: string, importerDir: string): boolean {\n\tconst packageJsonPath = findPackageJson(pkgName, importerDir);\n\tif (!packageJsonPath) {\n\t\treturn false;\n\t}\n\n\ttry {\n\t\tconst pkg = JSON.parse(readFileSync(packageJsonPath, \"utf8\"));\n\t\tif (pkg.torpor === true || typeof pkg.torpor === \"string\") {\n\t\t\treturn true;\n\t\t}\n\t\treturn exportsIncludeTorp(pkg.exports);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Finds a package's package.json, either by resolving it through the\n * package's own exports map, or (when the exports map doesn't expose\n * `./package.json`) through a node_modules walk-up from the importing file\n */\nfunction findPackageJson(pkgName: string, importerDir: string): string | undefined {\n\ttry {\n\t\tconst require = createRequire(path.join(importerDir, \"index.js\"));\n\t\tconst packageJson = require.resolve(`${pkgName}/package.json`);\n\t\tif (path.isAbsolute(packageJson)) {\n\t\t\treturn packageJson;\n\t\t}\n\t} catch {\n\t\t// Fall through to the node_modules walk-up below\n\t}\n\n\tlet dir = importerDir;\n\twhile (true) {\n\t\tconst packageJson = path.join(dir, \"node_modules\", ...pkgName.split(\"/\"), \"package.json\");\n\t\tif (existsSync(packageJson)) {\n\t\t\treturn packageJson;\n\t\t}\n\t\tconst parent = path.dirname(dir);\n\t\tif (parent === dir) {\n\t\t\treturn undefined;\n\t\t}\n\t\tdir = parent;\n\t}\n}\n\nfunction exportsIncludeTorp(exports: unknown): boolean {\n\tif (typeof exports === \"string\") {\n\t\treturn exports.endsWith(\".torp\");\n\t}\n\tif (Array.isArray(exports)) {\n\t\treturn exports.some(exportsIncludeTorp);\n\t}\n\tif (exports && typeof exports === \"object\") {\n\t\treturn Object.values(exports).some(exportsIncludeTorp);\n\t}\n\treturn false;\n}\n\n/*\nfunction printTransformed(transformed: string) {\n\tconsole.log(\n\t\ttransformed\n\t\t\t.split(\"\\n\")\n\t\t\t.map((l, i) => `${(i + 1).toString().padEnd(3)} ${l}`)\n\t\t\t.join(\"\\n\"),\n\t);\n}\n*/\n\nexport const unplugin: UnpluginInstance<Options | undefined, boolean> =\n\t/* #__PURE__ */ createUnplugin(unpluginFactory);\n\nexport default unplugin;\n"],"mappings":"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"}
|
package/dist/types.d.mts
CHANGED
|
@@ -9,7 +9,15 @@ interface Options {
|
|
|
9
9
|
*/
|
|
10
10
|
dev?: boolean;
|
|
11
11
|
/**
|
|
12
|
-
* Whether the plugin is running in a test context
|
|
12
|
+
* Whether the plugin is running in a test context. Components are then
|
|
13
|
+
* compiled for the server by default, so tests can call them as functions
|
|
14
|
+
* and assert on the rendered HTML. To mount a component client-side in
|
|
15
|
+
* the same test project, import it with a `?client` query (e.g.
|
|
16
|
+
* `Component.torp?client`); the override is passed on to any components
|
|
17
|
+
* it imports, including components from packages that ship `.torp` files
|
|
18
|
+
* (e.g. `@torpor/ui/*` or `phosphor-torpor/*`), whose re-export barrels
|
|
19
|
+
* get the query passed through as well. A `?server` query does the
|
|
20
|
+
* reverse.
|
|
13
21
|
*/
|
|
14
22
|
test?: boolean;
|
|
15
23
|
/**
|
package/dist/types.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";UAAyB;;;;EAIxB;;;;EAIA
|
|
1
|
+
{"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";UAAyB;;;;EAIxB;;;;EAIA;;;;;;;;;;;;EAYA;;;;;EAKA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@torpor/unplugin",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Unplugin package to compile Torpor components for Vite, Rollup, and other bundlers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"torpor",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
},
|
|
91
91
|
"dependencies": {
|
|
92
92
|
"unplugin": "^3.3.0",
|
|
93
|
-
"@torpor/view": "^1.1.
|
|
93
|
+
"@torpor/view": "^1.1.2"
|
|
94
94
|
},
|
|
95
95
|
"devDependencies": {
|
|
96
96
|
"@antfu/eslint-config": "^9.2.0",
|
|
@@ -103,13 +103,16 @@
|
|
|
103
103
|
"eslint": "^10.8.0",
|
|
104
104
|
"esno": "^4.8.0",
|
|
105
105
|
"fast-glob": "^3.3.3",
|
|
106
|
+
"jsdom": "^30.0.1",
|
|
106
107
|
"nodemon": "^3.1.14",
|
|
107
108
|
"rimraf": "^6.1.3",
|
|
108
109
|
"rollup": "^4.62.4",
|
|
109
110
|
"typescript": "^7.0.2",
|
|
110
111
|
"vite": "npm:@voidzero-dev/vite-plus-core@0.3.0",
|
|
111
112
|
"vite-plus": "0.3.0",
|
|
112
|
-
"webpack": "^5.109.2"
|
|
113
|
+
"webpack": "^5.109.2",
|
|
114
|
+
"@torpor/ui": "^1.1.6",
|
|
115
|
+
"torp-lib": "1.0.0"
|
|
113
116
|
},
|
|
114
117
|
"scripts": {
|
|
115
118
|
"check": "tsgo --noEmit && pnpm dlx oxlint --type-aware",
|
|
@@ -118,6 +121,7 @@
|
|
|
118
121
|
"lint": "eslint .",
|
|
119
122
|
"play": "npm -C playground run dev",
|
|
120
123
|
"release": "bumpp && npm publish",
|
|
121
|
-
"start": "esno src/index.ts"
|
|
124
|
+
"start": "esno src/index.ts",
|
|
125
|
+
"test": "vp test"
|
|
122
126
|
}
|
|
123
127
|
}
|