@evertondgn/solid-tooling 0.5.0 → 0.7.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 +543 -598
- package/app-env.d.ts +7 -0
- package/dist/architecture-plugin.mjs +1 -1
- package/dist/backend-plugin.mjs +139 -4
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +42 -0
- package/dist/{constants-Jqvtbvxu.mjs → constants-BZJm7ouk.mjs} +1 -1
- package/dist/css-filename-CEHQd2gz.mjs +40 -0
- package/dist/css-filename.mjs +1 -40
- package/dist/fmt-9dlUYOMk.mjs +34 -0
- package/dist/fmt.mjs +1 -34
- package/dist/i18n-generate.d.mts +1 -0
- package/dist/i18n-generate.mjs +23 -0
- package/dist/i18nPlugin-CS7Gob6R.mjs +109 -0
- package/dist/knip.d.mts +2 -1
- package/dist/knip.mjs +7 -2
- package/dist/{lint-UTfyg7AR.mjs → lint-ehdtTKfw.mjs} +33 -86
- package/dist/lint.mjs +1 -1
- package/dist/playwright-teardown.mjs +1 -1
- package/dist/playwright.d.mts +2 -1
- package/dist/playwright.mjs +4 -3
- package/dist/prepareMessages-BXmY6Ko2.mjs +222 -0
- package/dist/project-plugin.mjs +2 -2
- package/dist/protocol-DSNNUYja.mjs +18 -0
- package/dist/publication/read-llms-index.d.mts +12 -0
- package/dist/publication/read-llms-index.mjs +1 -0
- package/dist/publication-CRUTOB0B.mjs +885 -0
- package/dist/readLlmsIndex-Gj4iTxAk.mjs +27 -0
- package/dist/stylelint.mjs +2 -2
- package/dist/test-locale--tuH0Np5.mjs +3 -0
- package/dist/types-DHu2Z_ot.d.mts +49 -0
- package/dist/vite.d.mts +33 -7
- package/dist/vite.mjs +449 -88
- package/dist/vitest-BjLr7iLf.mjs +362 -0
- package/dist/vitest.d.mts +3 -7
- package/dist/vitest.mjs +1 -129
- package/lefthook.yml +3 -1
- package/node-env.d.ts +1 -0
- package/package.json +32 -39
- package/tsconfig.app.json +4 -9
- package/virtual-modules.d.ts +90 -0
package/dist/vite.mjs
CHANGED
|
@@ -1,19 +1,266 @@
|
|
|
1
|
-
import { fmt } from "./fmt.mjs";
|
|
2
|
-
import { t as
|
|
3
|
-
import {
|
|
1
|
+
import { t as fmt } from "./fmt-9dlUYOMk.mjs";
|
|
2
|
+
import { a as I18N_SETTINGS, i as validatePublicationFeatures, n as seoModule, o as hasI18n, r as localeModule, t as solidTest } from "./vitest-BjLr7iLf.mjs";
|
|
3
|
+
import { t as lint } from "./lint-ehdtTKfw.mjs";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { readFile } from "node:fs/promises";
|
|
6
5
|
import { env, loadEnvFile } from "node:process";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { readFile, realpath } from "node:fs/promises";
|
|
7
8
|
import { defineConfig, lazyPlugins, loadEnv } from "vite-plus";
|
|
9
|
+
import * as v from "valibot";
|
|
10
|
+
import { createHash } from "node:crypto";
|
|
11
|
+
// src/fonts/assets.ts
|
|
12
|
+
const RELATIVE_BASE = "./";
|
|
13
|
+
const PARENT_SEGMENT = /^(?:\.|%2e){2}$/iu;
|
|
14
|
+
const URL_IGNORED = /[\t\n\r]/gu;
|
|
15
|
+
const PERCENT_ESCAPE = /%[\da-f]{2}/giu;
|
|
16
|
+
const HEX_RADIX = 16;
|
|
17
|
+
const OUTPUT_URL = "file:///output/";
|
|
18
|
+
const HASH_LENGTH = 16;
|
|
19
|
+
const MAX_WEIGHT = 1e3;
|
|
20
|
+
const FORMATS = {
|
|
21
|
+
".woff2": "woff2",
|
|
22
|
+
".woff": "woff",
|
|
23
|
+
".ttf": "truetype",
|
|
24
|
+
".otf": "opentype"
|
|
25
|
+
};
|
|
26
|
+
const numericWeight = v.pipe(v.number(), v.integer(), v.minValue(1), v.maxValue(MAX_WEIGHT));
|
|
27
|
+
const textWeight = v.pipe(v.string(), v.regex(/^(?:normal|bold|(?:[1-9]\d{0,2}|1000)(?: (?:[1-9]\d{0,2}|1000))?)$/u));
|
|
28
|
+
const weightSchema = v.union([numericWeight, textWeight]);
|
|
29
|
+
const unicodeSchema = v.pipe(v.string(), v.regex(/^U\+[\dA-F?]{1,6}(?:-[\dA-F]{1,6})?(?:\s*,\s*U\+[\dA-F?]{1,6}(?:-[\dA-F]{1,6})?)*$/iu));
|
|
30
|
+
const fontSchema = v.strictObject({
|
|
31
|
+
path: v.pipe(v.string(), v.nonEmpty()),
|
|
32
|
+
weight: v.optional(weightSchema, "normal"),
|
|
33
|
+
style: v.optional(v.picklist([
|
|
34
|
+
"normal",
|
|
35
|
+
"italic",
|
|
36
|
+
"oblique"
|
|
37
|
+
]), "normal"),
|
|
38
|
+
display: v.optional(v.picklist([
|
|
39
|
+
"auto",
|
|
40
|
+
"block",
|
|
41
|
+
"swap",
|
|
42
|
+
"fallback",
|
|
43
|
+
"optional"
|
|
44
|
+
]), "swap"),
|
|
45
|
+
unicodeRange: v.optional(unicodeSchema),
|
|
46
|
+
preload: v.optional(v.boolean(), false)
|
|
47
|
+
});
|
|
48
|
+
const familyName = v.pipe(v.string(), v.nonEmpty(), v.check((value) => !/\p{Cc}/u.test(value), "Font families cannot contain control characters."));
|
|
49
|
+
const fileList = v.pipe(v.array(fontSchema), v.minLength(1));
|
|
50
|
+
const familySchema = v.strictObject({
|
|
51
|
+
family: familyName,
|
|
52
|
+
files: fileList
|
|
53
|
+
});
|
|
54
|
+
const familiesSchema = v.array(familySchema);
|
|
55
|
+
function hash(source) {
|
|
56
|
+
return createHash("sha256").update(source).digest("hex").slice(0, HASH_LENGTH);
|
|
57
|
+
}
|
|
58
|
+
function cssString(value) {
|
|
59
|
+
return `"${value.replaceAll("\\", String.raw`\\`).replaceAll("\"", String.raw`\"`)}"`;
|
|
60
|
+
}
|
|
61
|
+
function isInside(root, file) {
|
|
62
|
+
const relative = path.relative(root, file);
|
|
63
|
+
return relative !== "" && !relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative);
|
|
64
|
+
}
|
|
65
|
+
function isRelativeBase({ base, ssr }) {
|
|
66
|
+
if (base !== RELATIVE_BASE) return false;
|
|
67
|
+
if (ssr !== false) throw new Error("defineSolidApp fonts: a relative base needs ssr: false.");
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
function fontUrls({ base, ssr }) {
|
|
71
|
+
if (isRelativeBase({
|
|
72
|
+
base,
|
|
73
|
+
ssr
|
|
74
|
+
})) return {
|
|
75
|
+
css: (fileName) => path.posix.basename(fileName),
|
|
76
|
+
html: (fileName) => `${RELATIVE_BASE}${fileName}`
|
|
77
|
+
};
|
|
78
|
+
if (!base.startsWith("/") || base.startsWith("//") || /[?#]/u.test(base)) throw new Error("defineSolidApp fonts: base must be a same-origin absolute pathname.");
|
|
79
|
+
const url = (fileName) => `${base.replace(/\/$/u, "")}/${fileName}`;
|
|
80
|
+
return {
|
|
81
|
+
css: url,
|
|
82
|
+
html: url
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function decodeOnce(value) {
|
|
86
|
+
return value.replaceAll(PERCENT_ESCAPE, (escape) => String.fromCodePoint(Number.parseInt(escape.slice(1), HEX_RADIX)));
|
|
87
|
+
}
|
|
88
|
+
function hasParentSegment(value) {
|
|
89
|
+
return value.split(/[\\/]/u).some((segment) => PARENT_SEGMENT.test(segment.replaceAll(URL_IGNORED, "")));
|
|
90
|
+
}
|
|
91
|
+
async function readFontAssets({ root, fonts, base, assetsDir, ssr }) {
|
|
92
|
+
const urls = fontUrls({
|
|
93
|
+
base,
|
|
94
|
+
ssr
|
|
95
|
+
});
|
|
96
|
+
if (path.isAbsolute(assetsDir) || hasParentSegment(assetsDir) || hasParentSegment(decodeOnce(assetsDir)) || !new URL(`${OUTPUT_URL}${assetsDir}/file`).href.startsWith(OUTPUT_URL)) throw new Error("defineSolidApp fonts: assetsDir must stay inside the client output.");
|
|
97
|
+
const families = v.parse(familiesSchema, fonts);
|
|
98
|
+
const directory = await realpath(root);
|
|
99
|
+
const assets = /* @__PURE__ */ new Map();
|
|
100
|
+
const files = /* @__PURE__ */ new Set();
|
|
101
|
+
const preloads = /* @__PURE__ */ new Map();
|
|
102
|
+
const rules = [];
|
|
103
|
+
for (const { family, files: faces } of families) for (const face of faces) {
|
|
104
|
+
if (path.isAbsolute(face.path) || /[?#]/u.test(face.path)) throw new Error(`defineSolidApp fonts: ${face.path} must be relative to the application root, without a query or fragment.`);
|
|
105
|
+
const file = await realpath(path.resolve(directory, face.path));
|
|
106
|
+
if (!isInside(directory, file)) throw new Error(`defineSolidApp fonts: ${face.path} must stay inside the application root.`);
|
|
107
|
+
const extension = path.extname(file);
|
|
108
|
+
const format = FORMATS[extension];
|
|
109
|
+
if (!format) throw new Error(`defineSolidApp fonts: ${face.path} must be a woff2, woff, ttf or otf file.`);
|
|
110
|
+
const source = await readFile(file);
|
|
111
|
+
const key = `${hash(source)}${extension}`;
|
|
112
|
+
let asset = assets.get(key);
|
|
113
|
+
if (!asset) {
|
|
114
|
+
const name = path.basename(file, extension).replaceAll(/[^\w-]/gu, "_");
|
|
115
|
+
asset = {
|
|
116
|
+
fileName: path.posix.join(assetsDir, `${name}-${key}`),
|
|
117
|
+
source,
|
|
118
|
+
type: `font/${extension.slice(1)}`
|
|
119
|
+
};
|
|
120
|
+
assets.set(key, asset);
|
|
121
|
+
}
|
|
122
|
+
files.add(file);
|
|
123
|
+
const href = urls.html(asset.fileName);
|
|
124
|
+
if (face.preload) preloads.set(href, {
|
|
125
|
+
href,
|
|
126
|
+
type: asset.type
|
|
127
|
+
});
|
|
128
|
+
rules.push(`@font-face {\n font-family: ${cssString(family)};\n font-style: ${face.style};\n font-display: ${face.display};\n font-weight: ${face.weight};\n src: url(${cssString(urls.css(asset.fileName))}) format(${cssString(format)});${face.unicodeRange ? `\n unicode-range: ${face.unicodeRange};` : ""}\n}`);
|
|
129
|
+
}
|
|
130
|
+
const source = `${rules.join("\n\n")}\n`;
|
|
131
|
+
const fileName = path.posix.join(assetsDir, `solid-fonts-${hash(source)}.css`);
|
|
132
|
+
return {
|
|
133
|
+
assets: [...assets.values(), {
|
|
134
|
+
fileName,
|
|
135
|
+
source,
|
|
136
|
+
type: "text/css"
|
|
137
|
+
}],
|
|
138
|
+
files: [...files],
|
|
139
|
+
stylesheet: urls.html(fileName),
|
|
140
|
+
preloads: [...preloads.values()]
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
// src/fonts/index.ts
|
|
144
|
+
const MODULE_ID = "virtual:solid-app/fonts";
|
|
145
|
+
const RESOLVED_ID = "\0solid-app:fonts";
|
|
146
|
+
function fontsPlugin(options) {
|
|
147
|
+
if (options.fonts === void 0) return void 0;
|
|
148
|
+
if (!Array.isArray(options.fonts)) throw new TypeError("defineSolidApp fonts: fonts must be an array of family descriptors.");
|
|
149
|
+
if (options.fonts.length === 0) return void 0;
|
|
150
|
+
let assets;
|
|
151
|
+
let base = "/";
|
|
152
|
+
let assetsDir = "assets";
|
|
153
|
+
let isBuild = false;
|
|
154
|
+
const read = () => readFontAssets({
|
|
155
|
+
...options,
|
|
156
|
+
base,
|
|
157
|
+
assetsDir
|
|
158
|
+
});
|
|
159
|
+
return {
|
|
160
|
+
name: "solid-app:fonts",
|
|
161
|
+
async configResolved(config) {
|
|
162
|
+
({base} = config);
|
|
163
|
+
({assetsDir} = config.build);
|
|
164
|
+
isBuild = config.command === "build";
|
|
165
|
+
assets = read();
|
|
166
|
+
await assets;
|
|
167
|
+
},
|
|
168
|
+
async buildStart() {
|
|
169
|
+
const result = await assets;
|
|
170
|
+
for (const file of result.files) this.addWatchFile(file);
|
|
171
|
+
if (isBuild && this.environment.name === "client") for (const asset of result.assets) this.emitFile({
|
|
172
|
+
type: "asset",
|
|
173
|
+
fileName: asset.fileName,
|
|
174
|
+
source: asset.source
|
|
175
|
+
});
|
|
176
|
+
},
|
|
177
|
+
resolveId: (id) => id === MODULE_ID ? RESOLVED_ID : void 0,
|
|
178
|
+
async load(id) {
|
|
179
|
+
if (id !== RESOLVED_ID) return void 0;
|
|
180
|
+
const { stylesheet, preloads } = await assets;
|
|
181
|
+
return `export const stylesheet = ${JSON.stringify(stylesheet)}; export const preloads = ${JSON.stringify(preloads)}`;
|
|
182
|
+
},
|
|
183
|
+
async transformIndexHtml() {
|
|
184
|
+
if (options.ssr !== false) return void 0;
|
|
185
|
+
const { stylesheet, preloads } = await assets;
|
|
186
|
+
return [{
|
|
187
|
+
tag: "link",
|
|
188
|
+
attrs: {
|
|
189
|
+
rel: "stylesheet",
|
|
190
|
+
href: stylesheet
|
|
191
|
+
}
|
|
192
|
+
}, ...preloads.map(({ href, type }) => ({
|
|
193
|
+
tag: "link",
|
|
194
|
+
attrs: {
|
|
195
|
+
rel: "preload",
|
|
196
|
+
href,
|
|
197
|
+
as: "font",
|
|
198
|
+
type,
|
|
199
|
+
crossorigin: "anonymous"
|
|
200
|
+
}
|
|
201
|
+
}))];
|
|
202
|
+
},
|
|
203
|
+
configureServer(server) {
|
|
204
|
+
server.middlewares.use((request, response, next) => {
|
|
205
|
+
(async () => {
|
|
206
|
+
try {
|
|
207
|
+
const asset = (await assets).assets.find((item) => request.url?.split("?")[0] === `${base}${item.fileName}`);
|
|
208
|
+
if (!asset) {
|
|
209
|
+
next();
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
response.setHeader("Content-Type", asset.type);
|
|
213
|
+
response.setHeader("Cache-Control", "no-cache");
|
|
214
|
+
response.end(asset.source);
|
|
215
|
+
} catch (error) {
|
|
216
|
+
next(error);
|
|
217
|
+
}
|
|
218
|
+
})();
|
|
219
|
+
});
|
|
220
|
+
},
|
|
221
|
+
async handleHotUpdate({ file, server }) {
|
|
222
|
+
if (!(await assets).files.includes(file)) return void 0;
|
|
223
|
+
assets = read();
|
|
224
|
+
await assets;
|
|
225
|
+
for (const environment of Object.values(server.environments)) {
|
|
226
|
+
const module = environment.moduleGraph.getModuleById(RESOLVED_ID);
|
|
227
|
+
if (module) environment.moduleGraph.invalidateModule(module);
|
|
228
|
+
}
|
|
229
|
+
server.ws.send({ type: "full-reload" });
|
|
230
|
+
return [];
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
// src/vite/static-paths.ts
|
|
235
|
+
function isUnderLocale({ staticPath, locales }) {
|
|
236
|
+
const [firstSegment] = staticPath.split("/").slice(1);
|
|
237
|
+
return locales.includes(firstSegment ?? "");
|
|
238
|
+
}
|
|
239
|
+
function staticFilePaths({ options, locales }) {
|
|
240
|
+
const appPaths = [...(options.publicAssets ?? []).map((asset) => `${asset.baseURL}/**`), ...options.staticPaths ?? []];
|
|
241
|
+
const collision = appPaths.find((staticPath) => isUnderLocale({
|
|
242
|
+
staticPath,
|
|
243
|
+
locales
|
|
244
|
+
}));
|
|
245
|
+
if (collision) throw new Error(`defineSolidApp: the static path ${collision} is inside a locale folder; move it out of /${collision.split("/")[1]}/.`);
|
|
246
|
+
const seo = typeof options.seo === "object" ? options.seo : {};
|
|
247
|
+
const hasMarkdown = typeof seo.markdown === "object" && seo.markdown.static === true;
|
|
248
|
+
const markdownPaths = locales.length > 0 ? locales.map((locale) => `/${locale}/**.md`) : ["/**.md"];
|
|
249
|
+
const publishedPaths = [
|
|
250
|
+
...hasMarkdown ? markdownPaths : [],
|
|
251
|
+
...typeof seo.llms === "object" ? ["/llms.txt"] : [],
|
|
252
|
+
...typeof seo.llmsFull === "object" ? ["/llms-full.txt"] : [],
|
|
253
|
+
...seo.sitemap !== void 0 && seo.sitemap !== false ? ["/sitemap.xml", "/sitemaps/pages/**"] : [],
|
|
254
|
+
...hasMarkdown ? ["/_markdown/static.json"] : []
|
|
255
|
+
];
|
|
256
|
+
return [
|
|
257
|
+
"/assets/**",
|
|
258
|
+
...appPaths,
|
|
259
|
+
...publishedPaths
|
|
260
|
+
];
|
|
261
|
+
}
|
|
8
262
|
// src/vite/plugins.ts
|
|
9
263
|
const ROUTE_TYPES = "src/@types/routes.d.ts";
|
|
10
|
-
const SEO_FILE_PATHS = [
|
|
11
|
-
"/llms.txt",
|
|
12
|
-
"/llms-full.txt",
|
|
13
|
-
"/sitemap.xml",
|
|
14
|
-
"/sitemaps/pages/**",
|
|
15
|
-
"/_markdown/static.json"
|
|
16
|
-
];
|
|
17
264
|
async function loadPluginModules() {
|
|
18
265
|
const [solid, routing, icons, loaders] = await Promise.all([
|
|
19
266
|
import("@solidjs/vite-plugin"),
|
|
@@ -34,35 +281,29 @@ async function readExtension({ options, target, mode }) {
|
|
|
34
281
|
mode
|
|
35
282
|
}) ?? {};
|
|
36
283
|
}
|
|
284
|
+
async function i18nPlugins(options) {
|
|
285
|
+
if (options.i18nPlugin === false || !hasI18n(options.root)) return [];
|
|
286
|
+
const { i18nPlugin } = await import("./i18nPlugin-CS7Gob6R.mjs");
|
|
287
|
+
return i18nPlugin();
|
|
288
|
+
}
|
|
37
289
|
async function readLocales(root) {
|
|
290
|
+
let source;
|
|
291
|
+
try {
|
|
292
|
+
source = await readFile(path.join(root, I18N_SETTINGS), "utf8");
|
|
293
|
+
} catch (error) {
|
|
294
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") return [];
|
|
295
|
+
throw new Error(`defineSolidApp: cannot read the locales from ${I18N_SETTINGS}.`, { cause: error });
|
|
296
|
+
}
|
|
38
297
|
let settings;
|
|
39
298
|
try {
|
|
40
|
-
settings = JSON.parse(
|
|
299
|
+
settings = JSON.parse(source);
|
|
41
300
|
} catch (error) {
|
|
42
|
-
throw new Error(
|
|
301
|
+
throw new Error(`defineSolidApp: cannot read the locales from ${I18N_SETTINGS}.`, { cause: error });
|
|
43
302
|
}
|
|
44
303
|
const locales = settings !== null && typeof settings === "object" && "locales" in settings ? settings.locales : void 0;
|
|
45
|
-
if (!Array.isArray(locales) || !locales.every((locale) => typeof locale === "string")) throw new Error(
|
|
304
|
+
if (!Array.isArray(locales) || !locales.every((locale) => typeof locale === "string")) throw new Error(`defineSolidApp: ${I18N_SETTINGS} must list its locales as strings.`);
|
|
46
305
|
return locales;
|
|
47
306
|
}
|
|
48
|
-
function isUnderLocale({ staticPath, locales }) {
|
|
49
|
-
const [firstSegment] = staticPath.split("/").slice(1);
|
|
50
|
-
return locales.includes(firstSegment ?? "");
|
|
51
|
-
}
|
|
52
|
-
function staticFilePaths({ options, locales }) {
|
|
53
|
-
const appPaths = [...(options.publicAssets ?? []).map((asset) => `${asset.baseURL}/**`), ...options.staticPaths ?? []];
|
|
54
|
-
const collision = appPaths.find((staticPath) => isUnderLocale({
|
|
55
|
-
staticPath,
|
|
56
|
-
locales
|
|
57
|
-
}));
|
|
58
|
-
if (collision) throw new Error(`defineSolidApp: the static path ${collision} is inside a locale folder; move it out of /${collision.split("/")[1]}/.`);
|
|
59
|
-
return [
|
|
60
|
-
"/assets/**",
|
|
61
|
-
...appPaths,
|
|
62
|
-
...locales.map((locale) => `/${locale}/**.md`),
|
|
63
|
-
...SEO_FILE_PATHS
|
|
64
|
-
];
|
|
65
|
-
}
|
|
66
307
|
function iconsPlugin({ modules, options }) {
|
|
67
308
|
return modules.Icons({
|
|
68
309
|
compiler: "solid",
|
|
@@ -79,70 +320,149 @@ function staticHeaders({ securityHeaders }) {
|
|
|
79
320
|
"cross-origin-resource-policy": securityHeaders["cross-origin-resource-policy"]
|
|
80
321
|
};
|
|
81
322
|
}
|
|
323
|
+
function clientPlugins({ i18n, modules, extension, options, mode }) {
|
|
324
|
+
const e2eRoutes = mode === "e2e" ? options.e2e?.routes : void 0;
|
|
325
|
+
return [
|
|
326
|
+
localeModule(options),
|
|
327
|
+
fontsPlugin(options),
|
|
328
|
+
...extension.plugins ?? [],
|
|
329
|
+
...i18n,
|
|
330
|
+
iconsPlugin({
|
|
331
|
+
modules,
|
|
332
|
+
options
|
|
333
|
+
}),
|
|
334
|
+
modules.solid({ ssr: false }),
|
|
335
|
+
modules.fileRoutes(e2eRoutes ? {
|
|
336
|
+
dir: e2eRoutes,
|
|
337
|
+
httpMethods: true
|
|
338
|
+
} : {
|
|
339
|
+
types: ROUTE_TYPES,
|
|
340
|
+
httpMethods: true
|
|
341
|
+
})
|
|
342
|
+
];
|
|
343
|
+
}
|
|
344
|
+
function hasStaticMarkdown(options) {
|
|
345
|
+
return typeof options.seo === "object" && typeof options.seo.markdown === "object" && options.seo.markdown.static === true;
|
|
346
|
+
}
|
|
347
|
+
async function createPublication(options) {
|
|
348
|
+
const features = typeof options.seo === "object" ? options.seo : {};
|
|
349
|
+
const enabled = hasStaticMarkdown(options) || typeof features.llms === "object" || typeof features.sitemap === "object";
|
|
350
|
+
let publication;
|
|
351
|
+
if (enabled) {
|
|
352
|
+
const { publicationBuild } = await import("./publication-CRUTOB0B.mjs");
|
|
353
|
+
publication = publicationBuild(features);
|
|
354
|
+
}
|
|
355
|
+
return publication;
|
|
356
|
+
}
|
|
357
|
+
async function serverPlugins({ options, mode }) {
|
|
358
|
+
const [modules, { nitro }, extension, locales, i18n] = await Promise.all([
|
|
359
|
+
loadPluginModules(),
|
|
360
|
+
import("nitro/vite"),
|
|
361
|
+
readExtension({
|
|
362
|
+
options,
|
|
363
|
+
target: "app",
|
|
364
|
+
mode
|
|
365
|
+
}),
|
|
366
|
+
hasStaticMarkdown(options) ? readLocales(options.root) : [],
|
|
367
|
+
i18nPlugins(options)
|
|
368
|
+
]);
|
|
369
|
+
const isE2e = mode === "e2e";
|
|
370
|
+
const middleware = isE2e ? options.e2e?.middleware ?? options.middleware : options.middleware;
|
|
371
|
+
const e2eRoutes = isE2e ? options.e2e?.routes : void 0;
|
|
372
|
+
const headers = staticHeaders(options);
|
|
373
|
+
const publication = await createPublication(options);
|
|
374
|
+
return [
|
|
375
|
+
localeModule(options),
|
|
376
|
+
fontsPlugin(options),
|
|
377
|
+
seoModule({
|
|
378
|
+
root: options.root,
|
|
379
|
+
mode,
|
|
380
|
+
seo: options.seo
|
|
381
|
+
}),
|
|
382
|
+
...publication ? [publication.plugin] : [],
|
|
383
|
+
...extension.plugins ?? [],
|
|
384
|
+
...i18n,
|
|
385
|
+
iconsPlugin({
|
|
386
|
+
modules,
|
|
387
|
+
options
|
|
388
|
+
}),
|
|
389
|
+
modules.solid({
|
|
390
|
+
start: middleware ? { middleware } : {},
|
|
391
|
+
ssr: true,
|
|
392
|
+
serverFunctions: options.serverSetup ? { serverSetup: options.serverSetup } : true
|
|
393
|
+
}),
|
|
394
|
+
modules.fileRoutes(e2eRoutes ? {
|
|
395
|
+
dir: e2eRoutes,
|
|
396
|
+
httpMethods: true
|
|
397
|
+
} : {
|
|
398
|
+
types: ROUTE_TYPES,
|
|
399
|
+
httpMethods: true
|
|
400
|
+
}),
|
|
401
|
+
nitro({
|
|
402
|
+
serverEntry: false,
|
|
403
|
+
preset: "vercel",
|
|
404
|
+
modules: [...publication ? [publication.module] : [], ...extension.nitroModules ?? []],
|
|
405
|
+
publicAssets: options.publicAssets ?? [],
|
|
406
|
+
routeRules: {
|
|
407
|
+
...Object.fromEntries(staticFilePaths({
|
|
408
|
+
options,
|
|
409
|
+
locales
|
|
410
|
+
}).map((staticPath) => [staticPath, { headers }])),
|
|
411
|
+
...options.routeRules
|
|
412
|
+
}
|
|
413
|
+
})
|
|
414
|
+
];
|
|
415
|
+
}
|
|
82
416
|
function appPlugins({ options, mode }) {
|
|
83
417
|
return lazyPlugins(async () => {
|
|
84
|
-
|
|
418
|
+
if (options.ssr !== false) return serverPlugins({
|
|
419
|
+
options,
|
|
420
|
+
mode
|
|
421
|
+
});
|
|
422
|
+
const [modules, extension, i18n] = await Promise.all([
|
|
85
423
|
loadPluginModules(),
|
|
86
|
-
import("nitro/vite"),
|
|
87
424
|
readExtension({
|
|
88
425
|
options,
|
|
89
426
|
target: "app",
|
|
90
427
|
mode
|
|
91
428
|
}),
|
|
92
|
-
|
|
429
|
+
i18nPlugins(options)
|
|
93
430
|
]);
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
modules,
|
|
102
|
-
options
|
|
103
|
-
}),
|
|
104
|
-
modules.solid({
|
|
105
|
-
start: middleware ? { middleware } : {},
|
|
106
|
-
ssr: true,
|
|
107
|
-
serverFunctions: options.serverSetup ? { serverSetup: options.serverSetup } : true
|
|
108
|
-
}),
|
|
109
|
-
modules.fileRoutes(e2eRoutes ? {
|
|
110
|
-
dir: e2eRoutes,
|
|
111
|
-
httpMethods: true
|
|
112
|
-
} : {
|
|
113
|
-
types: ROUTE_TYPES,
|
|
114
|
-
httpMethods: true
|
|
115
|
-
}),
|
|
116
|
-
nitro({
|
|
117
|
-
serverEntry: false,
|
|
118
|
-
preset: "vercel",
|
|
119
|
-
modules: extension.nitroModules ?? [],
|
|
120
|
-
publicAssets: options.publicAssets ?? [],
|
|
121
|
-
routeRules: {
|
|
122
|
-
...Object.fromEntries(staticFilePaths({
|
|
123
|
-
options,
|
|
124
|
-
locales
|
|
125
|
-
}).map((staticPath) => [staticPath, { headers }])),
|
|
126
|
-
...options.routeRules
|
|
127
|
-
}
|
|
128
|
-
})
|
|
129
|
-
];
|
|
431
|
+
return clientPlugins({
|
|
432
|
+
modules,
|
|
433
|
+
extension,
|
|
434
|
+
options,
|
|
435
|
+
mode,
|
|
436
|
+
i18n
|
|
437
|
+
});
|
|
130
438
|
});
|
|
131
439
|
}
|
|
132
440
|
function testPlugins({ options, mode }) {
|
|
133
441
|
return lazyPlugins(async () => {
|
|
134
|
-
const [modules, extension] = await Promise.all([
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
442
|
+
const [modules, extension, i18n] = await Promise.all([
|
|
443
|
+
loadPluginModules(),
|
|
444
|
+
readExtension({
|
|
445
|
+
options,
|
|
446
|
+
target: "test",
|
|
447
|
+
mode
|
|
448
|
+
}),
|
|
449
|
+
i18nPlugins(options)
|
|
450
|
+
]);
|
|
139
451
|
return [
|
|
452
|
+
localeModule(options),
|
|
453
|
+
fontsPlugin(options),
|
|
454
|
+
...options.ssr === false ? [] : [seoModule({
|
|
455
|
+
root: options.root,
|
|
456
|
+
mode,
|
|
457
|
+
seo: options.seo
|
|
458
|
+
})],
|
|
140
459
|
...extension.plugins ?? [],
|
|
460
|
+
...i18n,
|
|
141
461
|
iconsPlugin({
|
|
142
462
|
modules,
|
|
143
463
|
options
|
|
144
464
|
}),
|
|
145
|
-
modules.solid({ serverFunctions: true }),
|
|
465
|
+
modules.solid(options.ssr === false ? {} : { serverFunctions: true }),
|
|
146
466
|
modules.fileRoutes({
|
|
147
467
|
types: ROUTE_TYPES,
|
|
148
468
|
httpMethods: true
|
|
@@ -152,6 +472,16 @@ function testPlugins({ options, mode }) {
|
|
|
152
472
|
}
|
|
153
473
|
// src/vite/tasks.ts
|
|
154
474
|
const TOOLING_CI_TEST_WORKERS = 3;
|
|
475
|
+
const TOOL_NAMES = [
|
|
476
|
+
"knip",
|
|
477
|
+
"tsc",
|
|
478
|
+
"playwright",
|
|
479
|
+
"lefthook",
|
|
480
|
+
"commitlint"
|
|
481
|
+
];
|
|
482
|
+
function toolCommand(name) {
|
|
483
|
+
return `node '${fileURLToPath(new URL("cli.mjs", import.meta.url)).replaceAll("'", String.raw`'\''`)}' ${name}`;
|
|
484
|
+
}
|
|
155
485
|
function fromWorkspace(pattern) {
|
|
156
486
|
return {
|
|
157
487
|
pattern,
|
|
@@ -166,9 +496,8 @@ function installInputs({ workspace }) {
|
|
|
166
496
|
];
|
|
167
497
|
return workspace ? patterns.map((pattern) => fromWorkspace(pattern)) : patterns;
|
|
168
498
|
}
|
|
169
|
-
function createTasks({ workspace, isCi }) {
|
|
499
|
+
function createTasks({ workspace, isCi, hasI18n }) {
|
|
170
500
|
const install = installInputs({ workspace });
|
|
171
|
-
const lockfile = workspace ? fromWorkspace("bun.lock") : "bun.lock";
|
|
172
501
|
const dependsOn = workspace ? { dependsOn: workspace.dependsOn } : {};
|
|
173
502
|
const projectCheckInputs = [
|
|
174
503
|
{ auto: true },
|
|
@@ -183,20 +512,25 @@ function createTasks({ workspace, isCi }) {
|
|
|
183
512
|
];
|
|
184
513
|
const testConcurrency = isCi ? ` --test-concurrency=${TOOLING_CI_TEST_WORKERS}` : "";
|
|
185
514
|
return {
|
|
186
|
-
|
|
187
|
-
command:
|
|
515
|
+
...Object.fromEntries(TOOL_NAMES.map((name) => [name, {
|
|
516
|
+
command: toolCommand(name),
|
|
517
|
+
cache: false
|
|
518
|
+
}])),
|
|
519
|
+
...hasI18n && { "i18n-generate": {
|
|
520
|
+
command: `bun '${fileURLToPath(new URL("i18n-generate.mjs", import.meta.url)).replaceAll("'", String.raw`'\''`)}'`,
|
|
521
|
+
...dependsOn,
|
|
188
522
|
cache: {
|
|
189
523
|
input: [
|
|
524
|
+
{ auto: true },
|
|
190
525
|
"src/**/i18n/*.json",
|
|
191
|
-
"tooling/i18n/**",
|
|
192
526
|
"project.inlang/settings.json",
|
|
193
527
|
"project.inlang/paraglide.config.ts",
|
|
194
528
|
"package.json",
|
|
195
|
-
|
|
529
|
+
...install
|
|
196
530
|
],
|
|
197
531
|
output: ["paraglide/**", ".paraglide/messages/**"]
|
|
198
532
|
}
|
|
199
|
-
},
|
|
533
|
+
} },
|
|
200
534
|
"lint-css": {
|
|
201
535
|
command: "bun tooling/css/check.ts",
|
|
202
536
|
...dependsOn,
|
|
@@ -212,7 +546,7 @@ function createTasks({ workspace, isCi }) {
|
|
|
212
546
|
}
|
|
213
547
|
},
|
|
214
548
|
"dead-code-project": {
|
|
215
|
-
command: "knip
|
|
549
|
+
command: `${toolCommand("knip")} --config tooling/knip.ts && ${toolCommand("knip")} --config tooling/knip.ts --strict`,
|
|
216
550
|
...dependsOn,
|
|
217
551
|
cache: {
|
|
218
552
|
env: ["NODE_ENV"],
|
|
@@ -246,7 +580,7 @@ function createTasks({ workspace, isCi }) {
|
|
|
246
580
|
}
|
|
247
581
|
},
|
|
248
582
|
"typecheck-node": {
|
|
249
|
-
command: "tsc --project tsconfig.node.json
|
|
583
|
+
command: `${toolCommand("tsc")} --project tsconfig.node.json`,
|
|
250
584
|
...dependsOn,
|
|
251
585
|
cache: {
|
|
252
586
|
input: [
|
|
@@ -277,12 +611,35 @@ function createTasks({ workspace, isCi }) {
|
|
|
277
611
|
function isPathPrefix(baseURL) {
|
|
278
612
|
return typeof baseURL === "string" && baseURL.startsWith("/") && baseURL.length > 1 && !baseURL.endsWith("/");
|
|
279
613
|
}
|
|
280
|
-
|
|
614
|
+
const SERVER_OPTIONS = [
|
|
615
|
+
"middleware",
|
|
616
|
+
"serverSetup",
|
|
617
|
+
"securityHeaders",
|
|
618
|
+
"publicAssets",
|
|
619
|
+
"staticPaths",
|
|
620
|
+
"routeRules",
|
|
621
|
+
"publication",
|
|
622
|
+
"seo"
|
|
623
|
+
];
|
|
624
|
+
function validateServerOptions(options) {
|
|
625
|
+
if ("publication" in options) throw new Error("publication was replaced by seo.markdown, seo.llms and seo.llmsFull");
|
|
626
|
+
if (typeof options.seo === "object") validatePublicationFeatures(options.seo);
|
|
281
627
|
const asset = (options.publicAssets ?? []).find(({ baseURL }) => !isPathPrefix(baseURL));
|
|
282
628
|
if (asset) throw new Error(`defineSolidApp: the publicAssets entry for ${asset.dir} needs a baseURL such as /images, without a trailing slash.`);
|
|
283
629
|
const staticPath = (options.staticPaths ?? []).find((pattern) => !pattern.startsWith("/"));
|
|
284
630
|
if (staticPath !== void 0) throw new Error(`defineSolidApp: the static path ${staticPath} must start with a slash.`);
|
|
285
631
|
}
|
|
632
|
+
function validateClientOptions(options) {
|
|
633
|
+
const serverOption = [...SERVER_OPTIONS.filter((key) => key in options), ..."middleware" in (options.e2e ?? {}) ? ["e2e.middleware"] : []].at(0);
|
|
634
|
+
if (serverOption !== void 0) throw new Error(`defineSolidApp: ${serverOption} needs SSR; remove it or remove ssr: false.`);
|
|
635
|
+
}
|
|
636
|
+
function validateOptions(options) {
|
|
637
|
+
if (options.ssr === false) {
|
|
638
|
+
validateClientOptions(options);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
validateServerOptions(options);
|
|
642
|
+
}
|
|
286
643
|
function loadTestEnvFile(root) {
|
|
287
644
|
try {
|
|
288
645
|
loadEnvFile(path.join(root, ".env.test"));
|
|
@@ -304,7 +661,8 @@ function defineSolidApp(options) {
|
|
|
304
661
|
run: { tasks: {
|
|
305
662
|
...createTasks({
|
|
306
663
|
workspace: options.workspace,
|
|
307
|
-
isCi: Boolean(env.CI)
|
|
664
|
+
isCi: Boolean(env.CI),
|
|
665
|
+
hasI18n: hasI18n(root)
|
|
308
666
|
}),
|
|
309
667
|
...options.tasks
|
|
310
668
|
} },
|
|
@@ -329,7 +687,10 @@ function defineSolidApp(options) {
|
|
|
329
687
|
fmt,
|
|
330
688
|
lint,
|
|
331
689
|
test: solidTest({
|
|
690
|
+
...options.test,
|
|
332
691
|
root,
|
|
692
|
+
language: options.language,
|
|
693
|
+
seo: options.ssr === false ? void 0 : options.seo,
|
|
333
694
|
plugins: () => testPlugins({
|
|
334
695
|
options,
|
|
335
696
|
mode
|