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