sv 0.9.6 → 0.9.7
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/bin.d.ts +1 -0
- package/dist/bin.js +320 -22950
- package/dist/{package-manager-DO5R9a6p.js → create-CyyvJXoi.js} +24847 -26795
- package/dist/install-DE_JOizJ.js +3180 -0
- package/dist/lib/index.d.ts +173 -0
- package/dist/lib/index.js +4 -0
- package/dist/lib/testing.d.ts +51 -0
- package/dist/{testing.js → lib/testing.js} +771 -808
- package/dist/shared.json +2 -2
- package/dist/templates/demo/package.json +7 -7
- package/dist/templates/library/package.json +8 -8
- package/dist/templates/minimal/package.json +5 -5
- package/package.json +6 -6
- package/dist/addons-C1BtYuVp.js +0 -5806
- package/dist/index.d.ts +0 -4
- package/dist/index.js +0 -4
- package/dist/install.d.ts +0 -23
- package/dist/testing.d.ts +0 -31
|
@@ -0,0 +1,3180 @@
|
|
|
1
|
+
import { Element, MagicString, T, Tag, __toESM, any, be, detect, getUserAgent, parseCss, parseHtml, parseHtml$1, parseJson, parseScript, parseScript$1, parseSvelte, require_picocolors, resolveCommand, serializeScript, stripAst, walk } from "./create-CyyvJXoi.js";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
|
|
6
|
+
//#region ../core/addon/config.ts
|
|
7
|
+
function defineAddon(config) {
|
|
8
|
+
return config;
|
|
9
|
+
}
|
|
10
|
+
function defineAddonOptions() {
|
|
11
|
+
return createOptionBuilder({});
|
|
12
|
+
}
|
|
13
|
+
function createOptionBuilder(options$6) {
|
|
14
|
+
return {
|
|
15
|
+
add(key, question) {
|
|
16
|
+
const newOptions = {
|
|
17
|
+
...options$6,
|
|
18
|
+
[key]: question
|
|
19
|
+
};
|
|
20
|
+
return createOptionBuilder(newOptions);
|
|
21
|
+
},
|
|
22
|
+
build() {
|
|
23
|
+
return options$6;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region ../../node_modules/.pnpm/dedent@1.6.0/node_modules/dedent/dist/dedent.mjs
|
|
30
|
+
function ownKeys(object, enumerableOnly) {
|
|
31
|
+
var keys = Object.keys(object);
|
|
32
|
+
if (Object.getOwnPropertySymbols) {
|
|
33
|
+
var symbols = Object.getOwnPropertySymbols(object);
|
|
34
|
+
enumerableOnly && (symbols = symbols.filter(function(sym) {
|
|
35
|
+
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
|
|
36
|
+
})), keys.push.apply(keys, symbols);
|
|
37
|
+
}
|
|
38
|
+
return keys;
|
|
39
|
+
}
|
|
40
|
+
function _objectSpread(target) {
|
|
41
|
+
for (var i = 1; i < arguments.length; i++) {
|
|
42
|
+
var source = null != arguments[i] ? arguments[i] : {};
|
|
43
|
+
i % 2 ? ownKeys(Object(source), !0).forEach(function(key) {
|
|
44
|
+
_defineProperty(target, key, source[key]);
|
|
45
|
+
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function(key) {
|
|
46
|
+
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return target;
|
|
50
|
+
}
|
|
51
|
+
function _defineProperty(obj, key, value) {
|
|
52
|
+
key = _toPropertyKey(key);
|
|
53
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
54
|
+
value,
|
|
55
|
+
enumerable: true,
|
|
56
|
+
configurable: true,
|
|
57
|
+
writable: true
|
|
58
|
+
});
|
|
59
|
+
else obj[key] = value;
|
|
60
|
+
return obj;
|
|
61
|
+
}
|
|
62
|
+
function _toPropertyKey(arg) {
|
|
63
|
+
var key = _toPrimitive(arg, "string");
|
|
64
|
+
return typeof key === "symbol" ? key : String(key);
|
|
65
|
+
}
|
|
66
|
+
function _toPrimitive(input, hint) {
|
|
67
|
+
if (typeof input !== "object" || input === null) return input;
|
|
68
|
+
var prim = input[Symbol.toPrimitive];
|
|
69
|
+
if (prim !== void 0) {
|
|
70
|
+
var res = prim.call(input, hint || "default");
|
|
71
|
+
if (typeof res !== "object") return res;
|
|
72
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
73
|
+
}
|
|
74
|
+
return (hint === "string" ? String : Number)(input);
|
|
75
|
+
}
|
|
76
|
+
const dedent = createDedent({});
|
|
77
|
+
var dedent_default = dedent;
|
|
78
|
+
function createDedent(options$6) {
|
|
79
|
+
dedent$1.withOptions = (newOptions) => createDedent(_objectSpread(_objectSpread({}, options$6), newOptions));
|
|
80
|
+
return dedent$1;
|
|
81
|
+
function dedent$1(strings, ...values) {
|
|
82
|
+
const raw = typeof strings === "string" ? [strings] : strings.raw;
|
|
83
|
+
const { escapeSpecialCharacters = Array.isArray(strings), trimWhitespace = true } = options$6;
|
|
84
|
+
let result = "";
|
|
85
|
+
for (let i = 0; i < raw.length; i++) {
|
|
86
|
+
let next = raw[i];
|
|
87
|
+
if (escapeSpecialCharacters) next = next.replace(/\\\n[ \t]*/g, "").replace(/\\`/g, "`").replace(/\\\$/g, "$").replace(/\\\{/g, "{");
|
|
88
|
+
result += next;
|
|
89
|
+
if (i < values.length) result += values[i];
|
|
90
|
+
}
|
|
91
|
+
const lines = result.split("\n");
|
|
92
|
+
let mindent = null;
|
|
93
|
+
for (const l of lines) {
|
|
94
|
+
const m = l.match(/^(\s+)\S+/);
|
|
95
|
+
if (m) {
|
|
96
|
+
const indent = m[1].length;
|
|
97
|
+
if (!mindent) mindent = indent;
|
|
98
|
+
else mindent = Math.min(mindent, indent);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (mindent !== null) {
|
|
102
|
+
const m = mindent;
|
|
103
|
+
result = lines.map((l) => l[0] === " " || l[0] === " " ? l.slice(m) : l).join("\n");
|
|
104
|
+
}
|
|
105
|
+
if (trimWhitespace) result = result.trim();
|
|
106
|
+
if (escapeSpecialCharacters) result = result.replace(/\\n/g, "\n");
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region ../core/utils.ts
|
|
113
|
+
function createPrinter(...conditions) {
|
|
114
|
+
return conditions.map((condition) => {
|
|
115
|
+
return (content, alt = "") => condition ? content : alt;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
//#endregion
|
|
120
|
+
//#region ../core/common.ts
|
|
121
|
+
function splitVersion(str) {
|
|
122
|
+
const [major, minor, patch] = str?.split(".") ?? [];
|
|
123
|
+
function toVersionNumber(val) {
|
|
124
|
+
return val !== void 0 && val !== "" && !isNaN(Number(val)) ? Number(val) : void 0;
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
major: toVersionNumber(major),
|
|
128
|
+
minor: toVersionNumber(minor),
|
|
129
|
+
patch: toVersionNumber(patch)
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function isVersionUnsupportedBelow(versionStr, belowStr) {
|
|
133
|
+
const version = splitVersion(versionStr);
|
|
134
|
+
const below = splitVersion(belowStr);
|
|
135
|
+
if (version.major === void 0 || below.major === void 0) return void 0;
|
|
136
|
+
if (version.major < below.major) return true;
|
|
137
|
+
if (version.major > below.major) return false;
|
|
138
|
+
if (version.minor === void 0 || below.minor === void 0) if (version.major === below.major) return false;
|
|
139
|
+
else return true;
|
|
140
|
+
if (version.minor < below.minor) return true;
|
|
141
|
+
if (version.minor > below.minor) return false;
|
|
142
|
+
if (version.patch === void 0 || below.patch === void 0) if (version.minor === below.minor) return false;
|
|
143
|
+
else return true;
|
|
144
|
+
if (version.patch < below.patch) return true;
|
|
145
|
+
if (version.patch > below.patch) return false;
|
|
146
|
+
if (version.patch === below.patch) return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
//#endregion
|
|
150
|
+
//#region ../core/index.ts
|
|
151
|
+
var import_picocolors$2 = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
152
|
+
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region ../../node_modules/.pnpm/decircular@1.0.0/node_modules/decircular/index.js
|
|
155
|
+
function decircular(object) {
|
|
156
|
+
const seenObjects = /* @__PURE__ */ new WeakMap();
|
|
157
|
+
function internalDecircular(value, path$1 = []) {
|
|
158
|
+
if (!(value !== null && typeof value === "object")) return value;
|
|
159
|
+
const existingPath = seenObjects.get(value);
|
|
160
|
+
if (existingPath) return `[Circular *${existingPath.join(".")}]`;
|
|
161
|
+
seenObjects.set(value, path$1);
|
|
162
|
+
const newValue = Array.isArray(value) ? [] : {};
|
|
163
|
+
for (const [key2, value2] of Object.entries(value)) newValue[key2] = internalDecircular(value2, [...path$1, key2]);
|
|
164
|
+
seenObjects.delete(value);
|
|
165
|
+
return newValue;
|
|
166
|
+
}
|
|
167
|
+
return internalDecircular(object);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
//#endregion
|
|
171
|
+
//#region ../core/tooling/js/common.ts
|
|
172
|
+
function addJsDocTypeComment(node, options$6) {
|
|
173
|
+
const comment = {
|
|
174
|
+
type: "Block",
|
|
175
|
+
value: `* @type {${options$6.type}} `
|
|
176
|
+
};
|
|
177
|
+
addComment(node, comment);
|
|
178
|
+
}
|
|
179
|
+
function addComment(node, comment) {
|
|
180
|
+
node.leadingComments ??= [];
|
|
181
|
+
if (!node.leadingComments.find((item) => item.type === "Block" && item.value === comment.value)) node.leadingComments.push(comment);
|
|
182
|
+
}
|
|
183
|
+
function createSpread(argument) {
|
|
184
|
+
return {
|
|
185
|
+
type: "SpreadElement",
|
|
186
|
+
argument
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function createLiteral(value) {
|
|
190
|
+
return {
|
|
191
|
+
type: "Literal",
|
|
192
|
+
value: value ?? null
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function areNodesEqual(node, otherNode) {
|
|
196
|
+
const nodeClone = stripAst(decircular(node), ["loc", "raw"]);
|
|
197
|
+
const otherNodeClone = stripAst(decircular(otherNode), ["loc", "raw"]);
|
|
198
|
+
return serializeScript(nodeClone) === serializeScript(otherNodeClone);
|
|
199
|
+
}
|
|
200
|
+
function appendFromString(node, options$6) {
|
|
201
|
+
const program = parseScript$1(dedent_default(options$6.code));
|
|
202
|
+
for (const childNode of program.body) node.body.push(childNode);
|
|
203
|
+
}
|
|
204
|
+
function parseExpression(code) {
|
|
205
|
+
const program = parseScript$1(dedent_default(code));
|
|
206
|
+
stripAst(program, ["raw"]);
|
|
207
|
+
const statement = program.body[0];
|
|
208
|
+
if (statement.type !== "ExpressionStatement") throw new Error("Code provided was not an expression");
|
|
209
|
+
return statement.expression;
|
|
210
|
+
}
|
|
211
|
+
function parseStatement(code) {
|
|
212
|
+
return parseFromString(code);
|
|
213
|
+
}
|
|
214
|
+
function parseFromString(code) {
|
|
215
|
+
return parseScript$1(dedent_default(code)).body[0];
|
|
216
|
+
}
|
|
217
|
+
/** Appends the statement to body of the block if it doesn't already exist */
|
|
218
|
+
function appendStatement(node, options$6) {
|
|
219
|
+
if (!contains(node, options$6.statement)) node.body.push(options$6.statement);
|
|
220
|
+
}
|
|
221
|
+
/** Returns `true` if the provided node exists in the AST */
|
|
222
|
+
function contains(node, targetNode) {
|
|
223
|
+
let found = false;
|
|
224
|
+
walk(node, null, { _(currentNode, { next, stop }) {
|
|
225
|
+
if (currentNode.type === targetNode.type) {
|
|
226
|
+
found = areNodesEqual(currentNode, targetNode);
|
|
227
|
+
if (found) stop();
|
|
228
|
+
}
|
|
229
|
+
next();
|
|
230
|
+
} });
|
|
231
|
+
return found;
|
|
232
|
+
}
|
|
233
|
+
function hasTypeProperty(node, options$6) {
|
|
234
|
+
return node.type === "TSPropertySignature" && node.key.type === "Identifier" && node.key.name === options$6.name;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
//#endregion
|
|
238
|
+
//#region ../core/tooling/js/array.ts
|
|
239
|
+
function create$1() {
|
|
240
|
+
return {
|
|
241
|
+
type: "ArrayExpression",
|
|
242
|
+
elements: []
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
function append(node, element) {
|
|
246
|
+
insertElement(node, element, { insertEnd: true });
|
|
247
|
+
}
|
|
248
|
+
function prepend(node, element) {
|
|
249
|
+
insertElement(node, element, { insertEnd: false });
|
|
250
|
+
}
|
|
251
|
+
function insertElement(node, element, options$6) {
|
|
252
|
+
if (typeof element === "string") {
|
|
253
|
+
let literal = node.elements.filter((item) => item !== null && item.type === "Literal").find((item) => item.value === element);
|
|
254
|
+
if (!literal) {
|
|
255
|
+
literal = {
|
|
256
|
+
type: "Literal",
|
|
257
|
+
value: element
|
|
258
|
+
};
|
|
259
|
+
if (options$6.insertEnd) node.elements.push(literal);
|
|
260
|
+
else node.elements.unshift(literal);
|
|
261
|
+
}
|
|
262
|
+
} else if (!node.elements.some((item) => item && areNodesEqual(element, item))) if (options$6.insertEnd) node.elements.push(element);
|
|
263
|
+
else node.elements.unshift(element);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
//#endregion
|
|
267
|
+
//#region ../core/tooling/js/object.ts
|
|
268
|
+
function property(node, options$6) {
|
|
269
|
+
return propertyNode(node, options$6).value;
|
|
270
|
+
}
|
|
271
|
+
function propertyNode(node, options$6) {
|
|
272
|
+
let prop = node.properties.filter((x$1) => x$1.type === "Property").find((x$1) => x$1.key.name === options$6.name);
|
|
273
|
+
if (!prop) {
|
|
274
|
+
let isShorthand = false;
|
|
275
|
+
if (options$6.fallback.type === "Identifier") isShorthand = options$6.fallback.name === options$6.name;
|
|
276
|
+
prop = {
|
|
277
|
+
type: "Property",
|
|
278
|
+
shorthand: isShorthand,
|
|
279
|
+
key: {
|
|
280
|
+
type: "Identifier",
|
|
281
|
+
name: options$6.name
|
|
282
|
+
},
|
|
283
|
+
value: options$6.fallback,
|
|
284
|
+
kind: "init",
|
|
285
|
+
computed: false,
|
|
286
|
+
method: false
|
|
287
|
+
};
|
|
288
|
+
node.properties.push(prop);
|
|
289
|
+
}
|
|
290
|
+
return prop;
|
|
291
|
+
}
|
|
292
|
+
function create(properties) {
|
|
293
|
+
return populateObjectExpression({
|
|
294
|
+
objectExpression: {
|
|
295
|
+
type: "ObjectExpression",
|
|
296
|
+
properties: []
|
|
297
|
+
},
|
|
298
|
+
properties,
|
|
299
|
+
override: false
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
function overrideProperties(objectExpression, properties) {
|
|
303
|
+
populateObjectExpression({
|
|
304
|
+
objectExpression,
|
|
305
|
+
properties,
|
|
306
|
+
override: true
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
function overrideProperty(node, options$6) {
|
|
310
|
+
const prop = node.properties.filter((x$1) => x$1.type === "Property").find((x$1) => x$1.key.name === options$6.name);
|
|
311
|
+
if (!prop) return property(node, {
|
|
312
|
+
name: options$6.name,
|
|
313
|
+
fallback: options$6.value
|
|
314
|
+
});
|
|
315
|
+
prop.value = options$6.value;
|
|
316
|
+
return options$6.value;
|
|
317
|
+
}
|
|
318
|
+
function populateObjectExpression(options$6) {
|
|
319
|
+
const getExpression = (value, existingExpression) => {
|
|
320
|
+
let expression;
|
|
321
|
+
if (Array.isArray(value)) {
|
|
322
|
+
expression = create$1();
|
|
323
|
+
for (const v of value) append(expression, getExpression(v));
|
|
324
|
+
} else if (typeof value === "object" && value !== null) if (value.type !== void 0) expression = value;
|
|
325
|
+
else if (options$6.override && existingExpression && existingExpression.type === "ObjectExpression") expression = populateObjectExpression({
|
|
326
|
+
objectExpression: existingExpression,
|
|
327
|
+
properties: value,
|
|
328
|
+
override: options$6.override
|
|
329
|
+
});
|
|
330
|
+
else expression = populateObjectExpression({
|
|
331
|
+
objectExpression: create({}),
|
|
332
|
+
properties: value,
|
|
333
|
+
override: options$6.override
|
|
334
|
+
});
|
|
335
|
+
else expression = createLiteral(value);
|
|
336
|
+
return expression;
|
|
337
|
+
};
|
|
338
|
+
for (const [prop, value] of Object.entries(options$6.properties)) {
|
|
339
|
+
if (value === void 0) continue;
|
|
340
|
+
if (options$6.override) {
|
|
341
|
+
const existingProperty = options$6.objectExpression.properties.filter((x$1) => x$1.type === "Property").find((x$1) => x$1.key.name === prop);
|
|
342
|
+
const existingExpression = existingProperty?.value.type === "ObjectExpression" ? existingProperty.value : void 0;
|
|
343
|
+
overrideProperty(options$6.objectExpression, {
|
|
344
|
+
name: prop,
|
|
345
|
+
value: getExpression(value, existingExpression)
|
|
346
|
+
});
|
|
347
|
+
} else property(options$6.objectExpression, {
|
|
348
|
+
name: prop,
|
|
349
|
+
fallback: getExpression(value)
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
return options$6.objectExpression;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
//#endregion
|
|
356
|
+
//#region ../core/tooling/js/function.ts
|
|
357
|
+
function createCall(options$6) {
|
|
358
|
+
const callExpression = {
|
|
359
|
+
type: "CallExpression",
|
|
360
|
+
callee: {
|
|
361
|
+
type: "Identifier",
|
|
362
|
+
name: options$6.name
|
|
363
|
+
},
|
|
364
|
+
arguments: [],
|
|
365
|
+
optional: false
|
|
366
|
+
};
|
|
367
|
+
for (const arg of options$6.args) {
|
|
368
|
+
let argNode;
|
|
369
|
+
if (options$6.useIdentifiers) argNode = {
|
|
370
|
+
type: "Identifier",
|
|
371
|
+
name: arg
|
|
372
|
+
};
|
|
373
|
+
else argNode = {
|
|
374
|
+
type: "Literal",
|
|
375
|
+
value: arg
|
|
376
|
+
};
|
|
377
|
+
callExpression.arguments.push(argNode);
|
|
378
|
+
}
|
|
379
|
+
return callExpression;
|
|
380
|
+
}
|
|
381
|
+
function getArgument(node, options$6) {
|
|
382
|
+
if (options$6.index < node.arguments.length) return node.arguments[options$6.index];
|
|
383
|
+
node.arguments.push(options$6.fallback);
|
|
384
|
+
return options$6.fallback;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
//#endregion
|
|
388
|
+
//#region ../core/tooling/js/imports.ts
|
|
389
|
+
function addEmpty(node, options$6) {
|
|
390
|
+
const expectedImportDeclaration = {
|
|
391
|
+
type: "ImportDeclaration",
|
|
392
|
+
source: {
|
|
393
|
+
type: "Literal",
|
|
394
|
+
value: options$6.from
|
|
395
|
+
},
|
|
396
|
+
specifiers: [],
|
|
397
|
+
attributes: [],
|
|
398
|
+
importKind: "value"
|
|
399
|
+
};
|
|
400
|
+
addImportIfNecessary(node, expectedImportDeclaration);
|
|
401
|
+
}
|
|
402
|
+
function addNamespace(node, options$6) {
|
|
403
|
+
const expectedImportDeclaration = {
|
|
404
|
+
type: "ImportDeclaration",
|
|
405
|
+
importKind: "value",
|
|
406
|
+
source: {
|
|
407
|
+
type: "Literal",
|
|
408
|
+
value: options$6.from
|
|
409
|
+
},
|
|
410
|
+
specifiers: [{
|
|
411
|
+
type: "ImportNamespaceSpecifier",
|
|
412
|
+
local: {
|
|
413
|
+
type: "Identifier",
|
|
414
|
+
name: options$6.as
|
|
415
|
+
}
|
|
416
|
+
}],
|
|
417
|
+
attributes: []
|
|
418
|
+
};
|
|
419
|
+
addImportIfNecessary(node, expectedImportDeclaration);
|
|
420
|
+
}
|
|
421
|
+
function addDefault(node, options$6) {
|
|
422
|
+
const expectedImportDeclaration = {
|
|
423
|
+
type: "ImportDeclaration",
|
|
424
|
+
source: {
|
|
425
|
+
type: "Literal",
|
|
426
|
+
value: options$6.from
|
|
427
|
+
},
|
|
428
|
+
specifiers: [{
|
|
429
|
+
type: "ImportDefaultSpecifier",
|
|
430
|
+
local: {
|
|
431
|
+
type: "Identifier",
|
|
432
|
+
name: options$6.as
|
|
433
|
+
}
|
|
434
|
+
}],
|
|
435
|
+
attributes: [],
|
|
436
|
+
importKind: "value"
|
|
437
|
+
};
|
|
438
|
+
addImportIfNecessary(node, expectedImportDeclaration);
|
|
439
|
+
}
|
|
440
|
+
function addNamed(node, options$6) {
|
|
441
|
+
const o_imports = Array.isArray(options$6.imports) ? Object.fromEntries(options$6.imports.map((n) => [n, n])) : options$6.imports;
|
|
442
|
+
const specifiers = Object.entries(o_imports).map(([key, value]) => {
|
|
443
|
+
return {
|
|
444
|
+
type: "ImportSpecifier",
|
|
445
|
+
imported: {
|
|
446
|
+
type: "Identifier",
|
|
447
|
+
name: key
|
|
448
|
+
},
|
|
449
|
+
local: {
|
|
450
|
+
type: "Identifier",
|
|
451
|
+
name: value
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
});
|
|
455
|
+
let importDecl;
|
|
456
|
+
walk(node, null, { ImportDeclaration(declaration$1) {
|
|
457
|
+
if (declaration$1.source.value === options$6.from && declaration$1.specifiers) importDecl = declaration$1;
|
|
458
|
+
} });
|
|
459
|
+
if (importDecl) {
|
|
460
|
+
specifiers.forEach((specifierToAdd) => {
|
|
461
|
+
if (importDecl?.specifiers?.every((existingSpecifier) => existingSpecifier.type === "ImportSpecifier" && existingSpecifier.local?.name !== specifierToAdd.local?.name && existingSpecifier.imported.type === "Identifier" && specifierToAdd.imported.type === "Identifier" && existingSpecifier.imported.name !== specifierToAdd.imported.name)) importDecl?.specifiers?.push(specifierToAdd);
|
|
462
|
+
});
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
const expectedImportDeclaration = {
|
|
466
|
+
type: "ImportDeclaration",
|
|
467
|
+
source: {
|
|
468
|
+
type: "Literal",
|
|
469
|
+
value: options$6.from
|
|
470
|
+
},
|
|
471
|
+
specifiers,
|
|
472
|
+
attributes: [],
|
|
473
|
+
importKind: options$6.isType ? "type" : "value"
|
|
474
|
+
};
|
|
475
|
+
node.body.unshift(expectedImportDeclaration);
|
|
476
|
+
}
|
|
477
|
+
function addImportIfNecessary(node, expectedImportDeclaration) {
|
|
478
|
+
if (!node.body.filter((item) => item.type === "ImportDeclaration").find((item) => areNodesEqual(item, expectedImportDeclaration))) node.body.unshift(expectedImportDeclaration);
|
|
479
|
+
}
|
|
480
|
+
function find(ast, options$6) {
|
|
481
|
+
let alias = options$6.name;
|
|
482
|
+
let statement;
|
|
483
|
+
walk(ast, null, { ImportDeclaration(node) {
|
|
484
|
+
if (node.specifiers && node.source.value === options$6.from) {
|
|
485
|
+
const specifier = node.specifiers.find((sp) => sp.type === "ImportSpecifier" && sp.imported.type === "Identifier" && sp.imported.name === options$6.name);
|
|
486
|
+
if (specifier) {
|
|
487
|
+
statement = node;
|
|
488
|
+
alias = specifier.local?.name ?? alias;
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
} });
|
|
493
|
+
if (statement) return {
|
|
494
|
+
statement,
|
|
495
|
+
alias
|
|
496
|
+
};
|
|
497
|
+
return {
|
|
498
|
+
statement: void 0,
|
|
499
|
+
alias: void 0
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
function remove(ast, options$6) {
|
|
503
|
+
const statement = options$6.statement ?? find(ast, {
|
|
504
|
+
name: options$6.name,
|
|
505
|
+
from: options$6.from
|
|
506
|
+
}).statement;
|
|
507
|
+
if (!statement) return;
|
|
508
|
+
if (statement.specifiers?.length === 1) {
|
|
509
|
+
const idxToRemove = ast.body.indexOf(statement);
|
|
510
|
+
ast.body.splice(idxToRemove, 1);
|
|
511
|
+
} else {
|
|
512
|
+
const idxToRemove = statement.specifiers?.findIndex((s) => s.type === "ImportSpecifier" && s.imported.type === "Identifier" && s.imported.name === options$6.name);
|
|
513
|
+
if (idxToRemove !== void 0 && idxToRemove !== -1) statement.specifiers?.splice(idxToRemove, 1);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
//#endregion
|
|
518
|
+
//#region ../core/tooling/js/variables.ts
|
|
519
|
+
function declaration(node, options$6) {
|
|
520
|
+
let declaration$1 = (node.type === "Program" ? node.body.filter((x$1) => x$1.type === "VariableDeclaration") : [node]).find((x$1) => {
|
|
521
|
+
return x$1.declarations[0].id.name === options$6.name;
|
|
522
|
+
});
|
|
523
|
+
if (declaration$1) return declaration$1;
|
|
524
|
+
declaration$1 = {
|
|
525
|
+
type: "VariableDeclaration",
|
|
526
|
+
kind: options$6.kind,
|
|
527
|
+
declarations: [{
|
|
528
|
+
type: "VariableDeclarator",
|
|
529
|
+
id: {
|
|
530
|
+
type: "Identifier",
|
|
531
|
+
name: options$6.name
|
|
532
|
+
},
|
|
533
|
+
init: options$6.value
|
|
534
|
+
}]
|
|
535
|
+
};
|
|
536
|
+
return declaration$1;
|
|
537
|
+
}
|
|
538
|
+
function createIdentifier(name) {
|
|
539
|
+
return {
|
|
540
|
+
type: "Identifier",
|
|
541
|
+
name
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
function typeAnnotateDeclarator(node, options$6) {
|
|
545
|
+
if (node.id.type === "Identifier") node.id.typeAnnotation = {
|
|
546
|
+
type: "TSTypeAnnotation",
|
|
547
|
+
typeAnnotation: {
|
|
548
|
+
type: "TSTypeReference",
|
|
549
|
+
typeName: {
|
|
550
|
+
type: "Identifier",
|
|
551
|
+
name: options$6.typeName
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
return node;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
//#endregion
|
|
559
|
+
//#region ../core/tooling/js/exports.ts
|
|
560
|
+
function createDefault(node, options$6) {
|
|
561
|
+
const existingNode = node.body.find((item) => item.type === "ExportDefaultDeclaration");
|
|
562
|
+
if (!existingNode) {
|
|
563
|
+
const exportNode = {
|
|
564
|
+
type: "ExportDefaultDeclaration",
|
|
565
|
+
declaration: options$6.fallback
|
|
566
|
+
};
|
|
567
|
+
node.body.push(exportNode);
|
|
568
|
+
return {
|
|
569
|
+
astNode: exportNode,
|
|
570
|
+
value: options$6.fallback,
|
|
571
|
+
isFallback: true
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
const exportDefaultDeclaration = existingNode;
|
|
575
|
+
if (exportDefaultDeclaration.declaration.type === "Identifier") {
|
|
576
|
+
const identifier = exportDefaultDeclaration.declaration;
|
|
577
|
+
let variableDeclaration;
|
|
578
|
+
let variableDeclarator;
|
|
579
|
+
for (const declaration$2 of node.body) {
|
|
580
|
+
if (declaration$2.type !== "VariableDeclaration") continue;
|
|
581
|
+
variableDeclarator = declaration$2.declarations.find((declarator) => declarator.type === "VariableDeclarator" && declarator.id.type === "Identifier" && declarator.id.name === identifier.name);
|
|
582
|
+
variableDeclaration = declaration$2;
|
|
583
|
+
}
|
|
584
|
+
if (!variableDeclaration || !variableDeclarator) throw new Error(`Unable to find exported variable '${identifier.name}'`);
|
|
585
|
+
const value = variableDeclarator.init;
|
|
586
|
+
return {
|
|
587
|
+
astNode: exportDefaultDeclaration,
|
|
588
|
+
value,
|
|
589
|
+
isFallback: false
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
const declaration$1 = exportDefaultDeclaration.declaration;
|
|
593
|
+
return {
|
|
594
|
+
astNode: exportDefaultDeclaration,
|
|
595
|
+
value: declaration$1,
|
|
596
|
+
isFallback: false
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
function createNamed(node, options$6) {
|
|
600
|
+
let namedExport = node.body.filter((item) => item.type === "ExportNamedDeclaration").find((exportNode) => {
|
|
601
|
+
return exportNode.declaration.declarations[0].id.name === options$6.name;
|
|
602
|
+
});
|
|
603
|
+
if (namedExport) return namedExport;
|
|
604
|
+
namedExport = {
|
|
605
|
+
type: "ExportNamedDeclaration",
|
|
606
|
+
declaration: options$6.fallback,
|
|
607
|
+
specifiers: [],
|
|
608
|
+
attributes: []
|
|
609
|
+
};
|
|
610
|
+
node.body.push(namedExport);
|
|
611
|
+
return namedExport;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
//#endregion
|
|
615
|
+
//#region ../core/tooling/js/kit.ts
|
|
616
|
+
function addGlobalAppInterface(node, options$6) {
|
|
617
|
+
let globalDecl = node.body.filter((n) => n.type === "TSModuleDeclaration").find((m) => m.global && m.declare);
|
|
618
|
+
if (!globalDecl) {
|
|
619
|
+
globalDecl = parseFromString("declare global {}");
|
|
620
|
+
node.body.push(globalDecl);
|
|
621
|
+
}
|
|
622
|
+
if (globalDecl.body?.type !== "TSModuleBlock") throw new Error("Unexpected body type of `declare global` in `src/app.d.ts`");
|
|
623
|
+
let app;
|
|
624
|
+
let interfaceNode;
|
|
625
|
+
walk(globalDecl, null, {
|
|
626
|
+
TSModuleDeclaration(node$1, { next }) {
|
|
627
|
+
if (node$1.id.type === "Identifier" && node$1.id.name === "App") app = node$1;
|
|
628
|
+
next();
|
|
629
|
+
},
|
|
630
|
+
TSInterfaceDeclaration(node$1) {
|
|
631
|
+
if (node$1.id.type === "Identifier" && node$1.id.name === options$6.name) interfaceNode = node$1;
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
if (!app) {
|
|
635
|
+
app = parseFromString("namespace App {}");
|
|
636
|
+
globalDecl.body.body.push(app);
|
|
637
|
+
}
|
|
638
|
+
if (app.body?.type !== "TSModuleBlock") throw new Error("Unexpected body type of `namespace App` in `src/app.d.ts`");
|
|
639
|
+
if (!interfaceNode) {
|
|
640
|
+
interfaceNode = parseFromString(`interface ${options$6.name} {}`);
|
|
641
|
+
app.body.body.push(interfaceNode);
|
|
642
|
+
}
|
|
643
|
+
return interfaceNode;
|
|
644
|
+
}
|
|
645
|
+
function addHooksHandle(node, options$6) {
|
|
646
|
+
if (options$6.typescript) addNamed(node, {
|
|
647
|
+
from: "@sveltejs/kit",
|
|
648
|
+
imports: { Handle: "Handle" },
|
|
649
|
+
isType: true
|
|
650
|
+
});
|
|
651
|
+
let isSpecifier = false;
|
|
652
|
+
let handleName = "handle";
|
|
653
|
+
let exportDecl;
|
|
654
|
+
let originalHandleDecl;
|
|
655
|
+
walk(node, null, { ExportNamedDeclaration(declaration$1) {
|
|
656
|
+
let maybeHandleDecl;
|
|
657
|
+
const handleSpecifier = declaration$1.specifiers?.find((specifier) => specifier.exported.type === "Identifier" && specifier.exported.name === "handle");
|
|
658
|
+
if (handleSpecifier && handleSpecifier.local.type === "Identifier" && handleSpecifier.exported.type === "Identifier") {
|
|
659
|
+
isSpecifier = true;
|
|
660
|
+
handleName = handleSpecifier.local?.name ?? handleSpecifier.exported.name;
|
|
661
|
+
const handleFunc = node.body.find((item) => isFunctionDeclaration(item, handleName));
|
|
662
|
+
const handleVar = node.body.find((item) => isVariableDeclaration(item, handleName));
|
|
663
|
+
maybeHandleDecl = handleFunc ?? handleVar;
|
|
664
|
+
}
|
|
665
|
+
maybeHandleDecl ??= declaration$1.declaration ?? void 0;
|
|
666
|
+
if (maybeHandleDecl && isVariableDeclaration(maybeHandleDecl, handleName)) {
|
|
667
|
+
exportDecl = declaration$1;
|
|
668
|
+
originalHandleDecl = maybeHandleDecl;
|
|
669
|
+
}
|
|
670
|
+
if (maybeHandleDecl && isFunctionDeclaration(maybeHandleDecl, handleName)) {
|
|
671
|
+
exportDecl = declaration$1;
|
|
672
|
+
originalHandleDecl = maybeHandleDecl;
|
|
673
|
+
}
|
|
674
|
+
} });
|
|
675
|
+
const newHandle = parseExpression(options$6.handleContent);
|
|
676
|
+
if (contains(node, newHandle)) return;
|
|
677
|
+
if (!originalHandleDecl || !exportDecl) {
|
|
678
|
+
const newHandleDecl$1 = declaration(node, {
|
|
679
|
+
kind: "const",
|
|
680
|
+
name: options$6.newHandleName,
|
|
681
|
+
value: newHandle
|
|
682
|
+
});
|
|
683
|
+
if (options$6.typescript) {
|
|
684
|
+
const declarator = newHandleDecl$1.declarations[0];
|
|
685
|
+
typeAnnotateDeclarator(declarator, { typeName: "Handle" });
|
|
686
|
+
}
|
|
687
|
+
node.body.push(newHandleDecl$1);
|
|
688
|
+
const handleDecl = declaration(node, {
|
|
689
|
+
kind: "const",
|
|
690
|
+
name: handleName,
|
|
691
|
+
value: createIdentifier(options$6.newHandleName)
|
|
692
|
+
});
|
|
693
|
+
if (options$6.typescript) {
|
|
694
|
+
const declarator = handleDecl.declarations[0];
|
|
695
|
+
typeAnnotateDeclarator(declarator, { typeName: "Handle" });
|
|
696
|
+
}
|
|
697
|
+
createNamed(node, {
|
|
698
|
+
name: handleName,
|
|
699
|
+
fallback: handleDecl
|
|
700
|
+
});
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
const newHandleDecl = declaration(node, {
|
|
704
|
+
kind: "const",
|
|
705
|
+
name: options$6.newHandleName,
|
|
706
|
+
value: newHandle
|
|
707
|
+
});
|
|
708
|
+
if (options$6.typescript) {
|
|
709
|
+
const declarator = newHandleDecl.declarations[0];
|
|
710
|
+
typeAnnotateDeclarator(declarator, { typeName: "Handle" });
|
|
711
|
+
}
|
|
712
|
+
let sequence;
|
|
713
|
+
if (originalHandleDecl.type === "VariableDeclaration") sequence = originalHandleDecl.declarations.find((declarator) => declarator.type === "VariableDeclarator" && usingSequence(declarator, handleName))?.init;
|
|
714
|
+
if (sequence) {
|
|
715
|
+
if (!sequence.arguments.some((arg) => arg.type === "Identifier" && arg.name === options$6.newHandleName)) sequence.arguments.push(createIdentifier(options$6.newHandleName));
|
|
716
|
+
node.body = node.body.filter((item) => item !== originalHandleDecl && item !== exportDecl && item !== newHandleDecl);
|
|
717
|
+
if (isSpecifier) node.body.push(newHandleDecl, originalHandleDecl, exportDecl);
|
|
718
|
+
else node.body.push(newHandleDecl, exportDecl);
|
|
719
|
+
}
|
|
720
|
+
const NEW_HANDLE_NAME = "originalHandle";
|
|
721
|
+
const sequenceCall = createCall({
|
|
722
|
+
name: "sequence",
|
|
723
|
+
args: [NEW_HANDLE_NAME, options$6.newHandleName],
|
|
724
|
+
useIdentifiers: true
|
|
725
|
+
});
|
|
726
|
+
const finalHandleDecl = declaration(node, {
|
|
727
|
+
kind: "const",
|
|
728
|
+
name: handleName,
|
|
729
|
+
value: sequenceCall
|
|
730
|
+
});
|
|
731
|
+
addNamed(node, {
|
|
732
|
+
from: "@sveltejs/kit/hooks",
|
|
733
|
+
imports: { sequence: "sequence" }
|
|
734
|
+
});
|
|
735
|
+
let renameRequired = false;
|
|
736
|
+
if (originalHandleDecl && isVariableDeclaration(originalHandleDecl, handleName)) {
|
|
737
|
+
const handle = getVariableDeclarator(originalHandleDecl, handleName);
|
|
738
|
+
if (handle && handle.id.type === "Identifier" && handle.init?.type !== "Identifier") {
|
|
739
|
+
renameRequired = true;
|
|
740
|
+
handle.id.name = NEW_HANDLE_NAME;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
if (originalHandleDecl && isFunctionDeclaration(originalHandleDecl, handleName)) {
|
|
744
|
+
renameRequired = true;
|
|
745
|
+
originalHandleDecl.id.name = NEW_HANDLE_NAME;
|
|
746
|
+
}
|
|
747
|
+
node.body = node.body.filter((item) => item !== originalHandleDecl && item !== exportDecl && item !== newHandleDecl);
|
|
748
|
+
if (isSpecifier) node.body.push(originalHandleDecl, newHandleDecl, finalHandleDecl, exportDecl);
|
|
749
|
+
if (exportDecl.declaration && renameRequired) {
|
|
750
|
+
node.body.push(exportDecl.declaration, newHandleDecl);
|
|
751
|
+
createNamed(node, {
|
|
752
|
+
name: handleName,
|
|
753
|
+
fallback: finalHandleDecl
|
|
754
|
+
});
|
|
755
|
+
} else if (exportDecl.declaration && isVariableDeclaration(originalHandleDecl, handleName)) {
|
|
756
|
+
const variableDeclarator = getVariableDeclarator(originalHandleDecl, handleName);
|
|
757
|
+
const sequenceCall$1 = createCall({
|
|
758
|
+
name: "sequence",
|
|
759
|
+
args: [(variableDeclarator?.init).name, options$6.newHandleName],
|
|
760
|
+
useIdentifiers: true
|
|
761
|
+
});
|
|
762
|
+
const finalHandleDecl$1 = declaration(node, {
|
|
763
|
+
kind: "const",
|
|
764
|
+
name: handleName,
|
|
765
|
+
value: sequenceCall$1
|
|
766
|
+
});
|
|
767
|
+
if (options$6.typescript) {
|
|
768
|
+
const declarator = finalHandleDecl$1.declarations[0];
|
|
769
|
+
typeAnnotateDeclarator(declarator, { typeName: "Handle" });
|
|
770
|
+
}
|
|
771
|
+
node.body.push(newHandleDecl);
|
|
772
|
+
createNamed(node, {
|
|
773
|
+
name: handleName,
|
|
774
|
+
fallback: finalHandleDecl$1
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
function usingSequence(node, handleName) {
|
|
779
|
+
return node.id.type === "Identifier" && node.id.name === handleName && node.init?.type === "CallExpression" && node.init.callee.type === "Identifier" && node.init.callee.name === "sequence";
|
|
780
|
+
}
|
|
781
|
+
function isVariableDeclaration(node, variableName) {
|
|
782
|
+
return node.type === "VariableDeclaration" && getVariableDeclarator(node, variableName) !== void 0;
|
|
783
|
+
}
|
|
784
|
+
function getVariableDeclarator(node, variableName) {
|
|
785
|
+
return node.declarations.find((d) => d.type === "VariableDeclarator" && d.id.type === "Identifier" && d.id.name === variableName);
|
|
786
|
+
}
|
|
787
|
+
function isFunctionDeclaration(node, funcName) {
|
|
788
|
+
return node.type === "FunctionDeclaration" && node.id?.name === funcName;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
//#endregion
|
|
792
|
+
//#region ../core/tooling/js/vite.ts
|
|
793
|
+
function isConfigWrapper(callExpression, knownWrappers) {
|
|
794
|
+
if (callExpression.callee.type !== "Identifier") return false;
|
|
795
|
+
const calleeName = callExpression.callee.name;
|
|
796
|
+
if (knownWrappers.includes(calleeName)) return true;
|
|
797
|
+
const isObjectCall = callExpression.arguments.length === 1 && callExpression.arguments[0]?.type === "ObjectExpression";
|
|
798
|
+
return knownWrappers.includes(calleeName) || isObjectCall;
|
|
799
|
+
}
|
|
800
|
+
function exportDefaultConfig(ast, options$6) {
|
|
801
|
+
const { fallback, ignoreWrapper } = options$6;
|
|
802
|
+
let fallbackExpression;
|
|
803
|
+
if (fallback) fallbackExpression = typeof fallback.code === "string" ? parseExpression(fallback.code) : fallback.code;
|
|
804
|
+
else fallbackExpression = create({});
|
|
805
|
+
const { value, isFallback } = createDefault(ast, { fallback: fallbackExpression });
|
|
806
|
+
if (isFallback) options$6.fallback?.additional?.(ast);
|
|
807
|
+
const rootObject = value.type === "TSSatisfiesExpression" ? value.expression : value;
|
|
808
|
+
let configObject;
|
|
809
|
+
if (!("arguments" in rootObject) || !Array.isArray(rootObject.arguments)) {
|
|
810
|
+
configObject = rootObject;
|
|
811
|
+
return configObject;
|
|
812
|
+
}
|
|
813
|
+
if (rootObject.type !== "CallExpression" || rootObject.callee.type !== "Identifier") {
|
|
814
|
+
configObject = rootObject;
|
|
815
|
+
return configObject;
|
|
816
|
+
}
|
|
817
|
+
if (!isConfigWrapper(rootObject, ignoreWrapper)) {
|
|
818
|
+
configObject = rootObject;
|
|
819
|
+
return configObject;
|
|
820
|
+
}
|
|
821
|
+
const firstArg = getArgument(rootObject, {
|
|
822
|
+
index: 0,
|
|
823
|
+
fallback: create({})
|
|
824
|
+
});
|
|
825
|
+
if (firstArg.type === "ArrowFunctionExpression") {
|
|
826
|
+
const arrowFunction = firstArg;
|
|
827
|
+
if (arrowFunction.body.type === "BlockStatement") {
|
|
828
|
+
const returnStatement = arrowFunction.body.body.find((stmt) => stmt.type === "ReturnStatement");
|
|
829
|
+
if (returnStatement && returnStatement.argument?.type === "ObjectExpression") configObject = returnStatement.argument;
|
|
830
|
+
else {
|
|
831
|
+
configObject = create({});
|
|
832
|
+
const newReturnStatement = {
|
|
833
|
+
type: "ReturnStatement",
|
|
834
|
+
argument: configObject
|
|
835
|
+
};
|
|
836
|
+
arrowFunction.body.body.push(newReturnStatement);
|
|
837
|
+
}
|
|
838
|
+
} else if (arrowFunction.body.type === "ObjectExpression") configObject = arrowFunction.body;
|
|
839
|
+
else {
|
|
840
|
+
configObject = create({});
|
|
841
|
+
arrowFunction.body = configObject;
|
|
842
|
+
arrowFunction.expression = true;
|
|
843
|
+
}
|
|
844
|
+
} else if (firstArg.type === "ObjectExpression") configObject = firstArg;
|
|
845
|
+
else configObject = create({});
|
|
846
|
+
return configObject;
|
|
847
|
+
}
|
|
848
|
+
function addInArrayOfObject(ast, options$6) {
|
|
849
|
+
const { code, arrayProperty, mode = "append" } = options$6;
|
|
850
|
+
const targetArray = property(ast, {
|
|
851
|
+
name: arrayProperty,
|
|
852
|
+
fallback: create$1()
|
|
853
|
+
});
|
|
854
|
+
const expression = parseExpression(code);
|
|
855
|
+
if (mode === "prepend") prepend(targetArray, expression);
|
|
856
|
+
else append(targetArray, expression);
|
|
857
|
+
}
|
|
858
|
+
const addPlugin = (ast, options$6) => {
|
|
859
|
+
const configObject = getConfig(ast);
|
|
860
|
+
addInArrayOfObject(configObject, {
|
|
861
|
+
arrayProperty: "plugins",
|
|
862
|
+
...options$6
|
|
863
|
+
});
|
|
864
|
+
};
|
|
865
|
+
const getConfig = (ast) => {
|
|
866
|
+
return exportDefaultConfig(ast, {
|
|
867
|
+
fallback: {
|
|
868
|
+
code: "defineConfig()",
|
|
869
|
+
additional: (ast$1) => addNamed(ast$1, {
|
|
870
|
+
imports: ["defineConfig"],
|
|
871
|
+
from: "vite"
|
|
872
|
+
})
|
|
873
|
+
},
|
|
874
|
+
ignoreWrapper: ["defineConfig"]
|
|
875
|
+
});
|
|
876
|
+
};
|
|
877
|
+
|
|
878
|
+
//#endregion
|
|
879
|
+
//#region ../addons/devtools-json/index.ts
|
|
880
|
+
var devtools_json_default = defineAddon({
|
|
881
|
+
id: "devtools-json",
|
|
882
|
+
shortDescription: "devtools json",
|
|
883
|
+
homepage: "https://github.com/ChromeDevTools/vite-plugin-devtools-json",
|
|
884
|
+
options: {},
|
|
885
|
+
run: ({ sv, viteConfigFile }) => {
|
|
886
|
+
sv.devDependency("vite-plugin-devtools-json", "^1.0.0");
|
|
887
|
+
sv.file(viteConfigFile, (content) => {
|
|
888
|
+
const { ast, generateCode } = parseScript(content);
|
|
889
|
+
const vitePluginName = "devtoolsJson";
|
|
890
|
+
addDefault(ast, {
|
|
891
|
+
as: vitePluginName,
|
|
892
|
+
from: "vite-plugin-devtools-json"
|
|
893
|
+
});
|
|
894
|
+
addPlugin(ast, { code: `${vitePluginName}()` });
|
|
895
|
+
return generateCode();
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
//#endregion
|
|
901
|
+
//#region ../addons/common.ts
|
|
902
|
+
function addEslintConfigPrettier(content) {
|
|
903
|
+
const { ast, generateCode } = parseScript(content);
|
|
904
|
+
const sveltePluginImport = ast.body.filter((n) => n.type === "ImportDeclaration").find((n) => n.type === "ImportDeclaration" && n.source.value === "eslint-plugin-svelte" && n.specifiers?.some((n$1) => n$1.type === "ImportDefaultSpecifier"));
|
|
905
|
+
let svelteImportName;
|
|
906
|
+
for (const specifier of sveltePluginImport?.specifiers ?? []) if (specifier.type === "ImportDefaultSpecifier" && specifier.local?.name) svelteImportName = specifier.local.name;
|
|
907
|
+
svelteImportName ??= "svelte";
|
|
908
|
+
addDefault(ast, {
|
|
909
|
+
from: "eslint-plugin-svelte",
|
|
910
|
+
as: svelteImportName
|
|
911
|
+
});
|
|
912
|
+
addDefault(ast, {
|
|
913
|
+
from: "eslint-config-prettier",
|
|
914
|
+
as: "prettier"
|
|
915
|
+
});
|
|
916
|
+
const fallbackConfig = parseExpression("[]");
|
|
917
|
+
const eslintConfig = createDefault(ast, { fallback: fallbackConfig }).value;
|
|
918
|
+
if (eslintConfig.type !== "ArrayExpression" && eslintConfig.type !== "CallExpression") return content;
|
|
919
|
+
const prettier = parseExpression("prettier");
|
|
920
|
+
const sveltePrettierConfig = parseExpression(`${svelteImportName}.configs.prettier`);
|
|
921
|
+
const configSpread = createSpread(sveltePrettierConfig);
|
|
922
|
+
const nodesToInsert = [];
|
|
923
|
+
if (!contains(eslintConfig, prettier)) nodesToInsert.push(prettier);
|
|
924
|
+
if (!contains(eslintConfig, configSpread)) nodesToInsert.push(configSpread);
|
|
925
|
+
const elements = eslintConfig.type === "ArrayExpression" ? eslintConfig.elements : eslintConfig.arguments;
|
|
926
|
+
const idx = elements.findIndex((el) => el?.type === "SpreadElement" && el.argument.type === "MemberExpression" && el.argument.object.type === "MemberExpression" && el.argument.object.property.type === "Identifier" && el.argument.object.property.name === "configs" && el.argument.object.object.type === "Identifier" && el.argument.object.object.name === svelteImportName);
|
|
927
|
+
if (idx !== -1) elements.splice(idx + 1, 0, ...nodesToInsert);
|
|
928
|
+
else elements.push(...nodesToInsert);
|
|
929
|
+
return generateCode();
|
|
930
|
+
}
|
|
931
|
+
function addToDemoPage(content, path$1) {
|
|
932
|
+
const { template, generateCode } = parseSvelte(content);
|
|
933
|
+
for (const node of template.ast.childNodes) if (node.type === "tag" && node.attribs["href"] === `/demo/${path$1}`) return content;
|
|
934
|
+
const newLine = template.source ? "\n" : "";
|
|
935
|
+
const src = template.source + `${newLine}<a href="/demo/${path$1}">${path$1}</a>`;
|
|
936
|
+
return generateCode({ template: src });
|
|
937
|
+
}
|
|
938
|
+
/**
|
|
939
|
+
* Returns the corresponding `@types/node` version for the version of Node.js running in the current process.
|
|
940
|
+
*
|
|
941
|
+
* If the installed version of Node.js is from a `Current` release, then the major is decremented to
|
|
942
|
+
* the nearest `LTS` release version.
|
|
943
|
+
*/
|
|
944
|
+
function getNodeTypesVersion() {
|
|
945
|
+
const nodeVersion = process.versions.node;
|
|
946
|
+
const isDenoOrBun = Boolean(process.versions.deno ?? process.versions.bun);
|
|
947
|
+
const [major] = nodeVersion.split(".");
|
|
948
|
+
const majorNum = Number(major);
|
|
949
|
+
const isEvenMajor = majorNum % 2 === 0;
|
|
950
|
+
if (!!process.release.lts || isDenoOrBun && isEvenMajor) return `^${major}`;
|
|
951
|
+
return `^${isEvenMajor ? majorNum - 2 : majorNum - 1}`;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
//#endregion
|
|
955
|
+
//#region ../addons/drizzle/index.ts
|
|
956
|
+
const PORTS = {
|
|
957
|
+
mysql: "3306",
|
|
958
|
+
postgresql: "5432",
|
|
959
|
+
sqlite: ""
|
|
960
|
+
};
|
|
961
|
+
const options$5 = defineAddonOptions().add("database", {
|
|
962
|
+
question: "Which database would you like to use?",
|
|
963
|
+
type: "select",
|
|
964
|
+
default: "sqlite",
|
|
965
|
+
options: [
|
|
966
|
+
{
|
|
967
|
+
value: "postgresql",
|
|
968
|
+
label: "PostgreSQL"
|
|
969
|
+
},
|
|
970
|
+
{
|
|
971
|
+
value: "mysql",
|
|
972
|
+
label: "MySQL"
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
value: "sqlite",
|
|
976
|
+
label: "SQLite"
|
|
977
|
+
}
|
|
978
|
+
]
|
|
979
|
+
}).add("postgresql", {
|
|
980
|
+
question: "Which PostgreSQL client would you like to use?",
|
|
981
|
+
type: "select",
|
|
982
|
+
group: "client",
|
|
983
|
+
default: "postgres.js",
|
|
984
|
+
options: [{
|
|
985
|
+
value: "postgres.js",
|
|
986
|
+
label: "Postgres.JS",
|
|
987
|
+
hint: "recommended for most users"
|
|
988
|
+
}, {
|
|
989
|
+
value: "neon",
|
|
990
|
+
label: "Neon",
|
|
991
|
+
hint: "popular hosted platform"
|
|
992
|
+
}],
|
|
993
|
+
condition: ({ database }) => database === "postgresql"
|
|
994
|
+
}).add("mysql", {
|
|
995
|
+
question: "Which MySQL client would you like to use?",
|
|
996
|
+
type: "select",
|
|
997
|
+
group: "client",
|
|
998
|
+
default: "mysql2",
|
|
999
|
+
options: [{
|
|
1000
|
+
value: "mysql2",
|
|
1001
|
+
hint: "recommended for most users"
|
|
1002
|
+
}, {
|
|
1003
|
+
value: "planetscale",
|
|
1004
|
+
label: "PlanetScale",
|
|
1005
|
+
hint: "popular hosted platform"
|
|
1006
|
+
}],
|
|
1007
|
+
condition: ({ database }) => database === "mysql"
|
|
1008
|
+
}).add("sqlite", {
|
|
1009
|
+
question: "Which SQLite client would you like to use?",
|
|
1010
|
+
type: "select",
|
|
1011
|
+
group: "client",
|
|
1012
|
+
default: "libsql",
|
|
1013
|
+
options: [
|
|
1014
|
+
{
|
|
1015
|
+
value: "better-sqlite3",
|
|
1016
|
+
hint: "for traditional Node environments"
|
|
1017
|
+
},
|
|
1018
|
+
{
|
|
1019
|
+
value: "libsql",
|
|
1020
|
+
label: "libSQL",
|
|
1021
|
+
hint: "for serverless environments"
|
|
1022
|
+
},
|
|
1023
|
+
{
|
|
1024
|
+
value: "turso",
|
|
1025
|
+
label: "Turso",
|
|
1026
|
+
hint: "popular hosted platform"
|
|
1027
|
+
}
|
|
1028
|
+
],
|
|
1029
|
+
condition: ({ database }) => database === "sqlite"
|
|
1030
|
+
}).add("docker", {
|
|
1031
|
+
question: "Do you want to run the database locally with docker-compose?",
|
|
1032
|
+
default: false,
|
|
1033
|
+
type: "boolean",
|
|
1034
|
+
condition: ({ database, mysql, postgresql }) => database === "mysql" && mysql === "mysql2" || database === "postgresql" && postgresql === "postgres.js"
|
|
1035
|
+
}).build();
|
|
1036
|
+
var drizzle_default = defineAddon({
|
|
1037
|
+
id: "drizzle",
|
|
1038
|
+
shortDescription: "database orm",
|
|
1039
|
+
homepage: "https://orm.drizzle.team",
|
|
1040
|
+
options: options$5,
|
|
1041
|
+
setup: ({ kit, unsupported, runsAfter, cwd, typescript }) => {
|
|
1042
|
+
runsAfter("prettier");
|
|
1043
|
+
const ext = typescript ? "ts" : "js";
|
|
1044
|
+
if (!kit) return unsupported("Requires SvelteKit");
|
|
1045
|
+
const baseDBPath = path.resolve(kit.libDirectory, "server", "db");
|
|
1046
|
+
const paths = {
|
|
1047
|
+
"drizzle config": path.relative(cwd, path.resolve(cwd, `drizzle.config.${ext}`)),
|
|
1048
|
+
"database schema": path.relative(cwd, path.resolve(baseDBPath, `schema.${ext}`)),
|
|
1049
|
+
database: path.relative(cwd, path.resolve(baseDBPath, `index.${ext}`))
|
|
1050
|
+
};
|
|
1051
|
+
for (const [fileType, filePath] of Object.entries(paths)) if (fs.existsSync(filePath)) unsupported(`Preexisting ${fileType} file at '${filePath}'`);
|
|
1052
|
+
},
|
|
1053
|
+
run: ({ sv, typescript, options: options$6, kit, dependencyVersion }) => {
|
|
1054
|
+
const ext = typescript ? "ts" : "js";
|
|
1055
|
+
sv.devDependency("drizzle-orm", "^0.44.5");
|
|
1056
|
+
sv.devDependency("drizzle-kit", "^0.31.4");
|
|
1057
|
+
sv.devDependency("@types/node", getNodeTypesVersion());
|
|
1058
|
+
if (options$6.mysql === "mysql2") sv.dependency("mysql2", "^3.15.0");
|
|
1059
|
+
if (options$6.mysql === "planetscale") sv.dependency("@planetscale/database", "^1.19.0");
|
|
1060
|
+
if (options$6.postgresql === "neon") sv.dependency("@neondatabase/serverless", "^1.0.1");
|
|
1061
|
+
if (options$6.postgresql === "postgres.js") sv.dependency("postgres", "^3.4.7");
|
|
1062
|
+
if (options$6.sqlite === "better-sqlite3") {
|
|
1063
|
+
sv.dependency("better-sqlite3", "^12.4.1");
|
|
1064
|
+
sv.devDependency("@types/better-sqlite3", "^7.6.13");
|
|
1065
|
+
sv.pnpmBuildDependency("better-sqlite3");
|
|
1066
|
+
}
|
|
1067
|
+
if (options$6.sqlite === "libsql" || options$6.sqlite === "turso") sv.devDependency("@libsql/client", "^0.15.15");
|
|
1068
|
+
sv.file(".env", (content) => generateEnvFileContent(content, options$6));
|
|
1069
|
+
sv.file(".env.example", (content) => generateEnvFileContent(content, options$6));
|
|
1070
|
+
if (options$6.docker && (options$6.mysql === "mysql2" || options$6.postgresql === "postgres.js")) sv.file("docker-compose.yml", (content) => {
|
|
1071
|
+
if (content.length > 0) return content;
|
|
1072
|
+
const imageName = options$6.database === "mysql" ? "mysql" : "postgres";
|
|
1073
|
+
const port = PORTS[options$6.database];
|
|
1074
|
+
const USER = "root";
|
|
1075
|
+
const PASSWORD = "mysecretpassword";
|
|
1076
|
+
const DB_NAME = "local";
|
|
1077
|
+
let dbSpecificContent = "";
|
|
1078
|
+
if (options$6.mysql === "mysql2") dbSpecificContent = `
|
|
1079
|
+
MYSQL_ROOT_PASSWORD: ${PASSWORD}
|
|
1080
|
+
MYSQL_DATABASE: ${DB_NAME}
|
|
1081
|
+
volumes:
|
|
1082
|
+
- mysqldata:/var/lib/mysql
|
|
1083
|
+
volumes:
|
|
1084
|
+
mysqldata:
|
|
1085
|
+
`;
|
|
1086
|
+
if (options$6.postgresql === "postgres.js") dbSpecificContent = `
|
|
1087
|
+
POSTGRES_USER: ${USER}
|
|
1088
|
+
POSTGRES_PASSWORD: ${PASSWORD}
|
|
1089
|
+
POSTGRES_DB: ${DB_NAME}
|
|
1090
|
+
volumes:
|
|
1091
|
+
- pgdata:/var/lib/postgresql/data
|
|
1092
|
+
volumes:
|
|
1093
|
+
pgdata:
|
|
1094
|
+
`;
|
|
1095
|
+
content = dedent_default`
|
|
1096
|
+
services:
|
|
1097
|
+
db:
|
|
1098
|
+
image: ${imageName}
|
|
1099
|
+
restart: always
|
|
1100
|
+
ports:
|
|
1101
|
+
- ${port}:${port}
|
|
1102
|
+
environment: ${dbSpecificContent}
|
|
1103
|
+
`;
|
|
1104
|
+
return content;
|
|
1105
|
+
});
|
|
1106
|
+
sv.file("package.json", (content) => {
|
|
1107
|
+
const { data, generateCode } = parseJson(content);
|
|
1108
|
+
data.scripts ??= {};
|
|
1109
|
+
const scripts = data.scripts;
|
|
1110
|
+
if (options$6.docker) scripts["db:start"] ??= "docker compose up";
|
|
1111
|
+
scripts["db:push"] ??= "drizzle-kit push";
|
|
1112
|
+
scripts["db:generate"] ??= "drizzle-kit generate";
|
|
1113
|
+
scripts["db:migrate"] ??= "drizzle-kit migrate";
|
|
1114
|
+
scripts["db:studio"] ??= "drizzle-kit studio";
|
|
1115
|
+
return generateCode();
|
|
1116
|
+
});
|
|
1117
|
+
if (Boolean(dependencyVersion("prettier"))) sv.file(".prettierignore", (content) => {
|
|
1118
|
+
if (!content.includes(`/drizzle/`)) return content.trimEnd() + "\n/drizzle/";
|
|
1119
|
+
return content;
|
|
1120
|
+
});
|
|
1121
|
+
if (options$6.database === "sqlite") sv.file(".gitignore", (content) => {
|
|
1122
|
+
if (content.length === 0) return content;
|
|
1123
|
+
if (!content.includes("\n*.db")) content = content.trimEnd() + "\n\n# SQLite\n*.db";
|
|
1124
|
+
return content;
|
|
1125
|
+
});
|
|
1126
|
+
sv.file(`drizzle.config.${ext}`, (content) => {
|
|
1127
|
+
const { ast, generateCode } = parseScript(content);
|
|
1128
|
+
addNamed(ast, {
|
|
1129
|
+
from: "drizzle-kit",
|
|
1130
|
+
imports: { defineConfig: "defineConfig" }
|
|
1131
|
+
});
|
|
1132
|
+
ast.body.push(parseStatement("if (!process.env.DATABASE_URL) throw new Error('DATABASE_URL is not set');"));
|
|
1133
|
+
createDefault(ast, { fallback: parseExpression(`
|
|
1134
|
+
defineConfig({
|
|
1135
|
+
schema: "./src/lib/server/db/schema.${typescript ? "ts" : "js"}",
|
|
1136
|
+
dialect: "${options$6.sqlite === "turso" ? "turso" : options$6.database}",
|
|
1137
|
+
dbCredentials: {
|
|
1138
|
+
${options$6.sqlite === "turso" ? "authToken: process.env.DATABASE_AUTH_TOKEN," : ""}
|
|
1139
|
+
url: process.env.DATABASE_URL
|
|
1140
|
+
},
|
|
1141
|
+
verbose: true,
|
|
1142
|
+
strict: true
|
|
1143
|
+
})`) });
|
|
1144
|
+
return generateCode();
|
|
1145
|
+
});
|
|
1146
|
+
sv.file(`${kit?.libDirectory}/server/db/schema.${ext}`, (content) => {
|
|
1147
|
+
const { ast, generateCode } = parseScript(content);
|
|
1148
|
+
let userSchemaExpression;
|
|
1149
|
+
if (options$6.database === "sqlite") {
|
|
1150
|
+
addNamed(ast, {
|
|
1151
|
+
from: "drizzle-orm/sqlite-core",
|
|
1152
|
+
imports: ["sqliteTable", "integer"]
|
|
1153
|
+
});
|
|
1154
|
+
userSchemaExpression = parseExpression(`sqliteTable('user', {
|
|
1155
|
+
id: integer('id').primaryKey(),
|
|
1156
|
+
age: integer('age')
|
|
1157
|
+
})`);
|
|
1158
|
+
}
|
|
1159
|
+
if (options$6.database === "mysql") {
|
|
1160
|
+
addNamed(ast, {
|
|
1161
|
+
from: "drizzle-orm/mysql-core",
|
|
1162
|
+
imports: [
|
|
1163
|
+
"mysqlTable",
|
|
1164
|
+
"serial",
|
|
1165
|
+
"int"
|
|
1166
|
+
]
|
|
1167
|
+
});
|
|
1168
|
+
userSchemaExpression = parseExpression(`mysqlTable('user', {
|
|
1169
|
+
id: serial('id').primaryKey(),
|
|
1170
|
+
age: int('age'),
|
|
1171
|
+
})`);
|
|
1172
|
+
}
|
|
1173
|
+
if (options$6.database === "postgresql") {
|
|
1174
|
+
addNamed(ast, {
|
|
1175
|
+
from: "drizzle-orm/pg-core",
|
|
1176
|
+
imports: [
|
|
1177
|
+
"pgTable",
|
|
1178
|
+
"serial",
|
|
1179
|
+
"integer"
|
|
1180
|
+
]
|
|
1181
|
+
});
|
|
1182
|
+
userSchemaExpression = parseExpression(`pgTable('user', {
|
|
1183
|
+
id: serial('id').primaryKey(),
|
|
1184
|
+
age: integer('age'),
|
|
1185
|
+
})`);
|
|
1186
|
+
}
|
|
1187
|
+
if (!userSchemaExpression) throw new Error("unreachable state...");
|
|
1188
|
+
const userIdentifier = declaration(ast, {
|
|
1189
|
+
kind: "const",
|
|
1190
|
+
name: "user",
|
|
1191
|
+
value: userSchemaExpression
|
|
1192
|
+
});
|
|
1193
|
+
createNamed(ast, {
|
|
1194
|
+
name: "user",
|
|
1195
|
+
fallback: userIdentifier
|
|
1196
|
+
});
|
|
1197
|
+
return generateCode();
|
|
1198
|
+
});
|
|
1199
|
+
sv.file(`${kit?.libDirectory}/server/db/index.${ext}`, (content) => {
|
|
1200
|
+
const { ast, generateCode } = parseScript(content);
|
|
1201
|
+
addNamed(ast, {
|
|
1202
|
+
from: "$env/dynamic/private",
|
|
1203
|
+
imports: ["env"]
|
|
1204
|
+
});
|
|
1205
|
+
addNamespace(ast, {
|
|
1206
|
+
from: "./schema",
|
|
1207
|
+
as: "schema"
|
|
1208
|
+
});
|
|
1209
|
+
const dbURLCheck = parseStatement("if (!env.DATABASE_URL) throw new Error('DATABASE_URL is not set');");
|
|
1210
|
+
ast.body.push(dbURLCheck);
|
|
1211
|
+
let clientExpression;
|
|
1212
|
+
if (options$6.sqlite === "better-sqlite3") {
|
|
1213
|
+
addDefault(ast, {
|
|
1214
|
+
from: "better-sqlite3",
|
|
1215
|
+
as: "Database"
|
|
1216
|
+
});
|
|
1217
|
+
addNamed(ast, {
|
|
1218
|
+
from: "drizzle-orm/better-sqlite3",
|
|
1219
|
+
imports: ["drizzle"]
|
|
1220
|
+
});
|
|
1221
|
+
clientExpression = parseExpression("new Database(env.DATABASE_URL)");
|
|
1222
|
+
}
|
|
1223
|
+
if (options$6.sqlite === "libsql" || options$6.sqlite === "turso") {
|
|
1224
|
+
addNamed(ast, {
|
|
1225
|
+
from: "@libsql/client",
|
|
1226
|
+
imports: ["createClient"]
|
|
1227
|
+
});
|
|
1228
|
+
addNamed(ast, {
|
|
1229
|
+
from: "drizzle-orm/libsql",
|
|
1230
|
+
imports: ["drizzle"]
|
|
1231
|
+
});
|
|
1232
|
+
if (options$6.sqlite === "turso") {
|
|
1233
|
+
addNamed(ast, {
|
|
1234
|
+
from: "$app/environment",
|
|
1235
|
+
imports: ["dev"]
|
|
1236
|
+
});
|
|
1237
|
+
const authTokenCheck = parseStatement("if (!dev && !env.DATABASE_AUTH_TOKEN) throw new Error('DATABASE_AUTH_TOKEN is not set');");
|
|
1238
|
+
ast.body.push(authTokenCheck);
|
|
1239
|
+
clientExpression = parseExpression("createClient({ url: env.DATABASE_URL, authToken: env.DATABASE_AUTH_TOKEN })");
|
|
1240
|
+
} else clientExpression = parseExpression("createClient({ url: env.DATABASE_URL })");
|
|
1241
|
+
}
|
|
1242
|
+
if (options$6.mysql === "mysql2" || options$6.mysql === "planetscale") {
|
|
1243
|
+
addDefault(ast, {
|
|
1244
|
+
from: "mysql2/promise",
|
|
1245
|
+
as: "mysql"
|
|
1246
|
+
});
|
|
1247
|
+
addNamed(ast, {
|
|
1248
|
+
from: "drizzle-orm/mysql2",
|
|
1249
|
+
imports: ["drizzle"]
|
|
1250
|
+
});
|
|
1251
|
+
clientExpression = parseExpression("mysql.createPool(env.DATABASE_URL)");
|
|
1252
|
+
}
|
|
1253
|
+
if (options$6.postgresql === "neon") {
|
|
1254
|
+
addNamed(ast, {
|
|
1255
|
+
from: "@neondatabase/serverless",
|
|
1256
|
+
imports: ["neon"]
|
|
1257
|
+
});
|
|
1258
|
+
addNamed(ast, {
|
|
1259
|
+
from: "drizzle-orm/neon-http",
|
|
1260
|
+
imports: ["drizzle"]
|
|
1261
|
+
});
|
|
1262
|
+
clientExpression = parseExpression("neon(env.DATABASE_URL)");
|
|
1263
|
+
}
|
|
1264
|
+
if (options$6.postgresql === "postgres.js") {
|
|
1265
|
+
addDefault(ast, {
|
|
1266
|
+
from: "postgres",
|
|
1267
|
+
as: "postgres"
|
|
1268
|
+
});
|
|
1269
|
+
addNamed(ast, {
|
|
1270
|
+
from: "drizzle-orm/postgres-js",
|
|
1271
|
+
imports: ["drizzle"]
|
|
1272
|
+
});
|
|
1273
|
+
clientExpression = parseExpression("postgres(env.DATABASE_URL)");
|
|
1274
|
+
}
|
|
1275
|
+
if (!clientExpression) throw new Error("unreachable state...");
|
|
1276
|
+
ast.body.push(declaration(ast, {
|
|
1277
|
+
kind: "const",
|
|
1278
|
+
name: "client",
|
|
1279
|
+
value: clientExpression
|
|
1280
|
+
}));
|
|
1281
|
+
const drizzleCall = createCall({
|
|
1282
|
+
name: "drizzle",
|
|
1283
|
+
args: ["client"],
|
|
1284
|
+
useIdentifiers: true
|
|
1285
|
+
});
|
|
1286
|
+
const paramObject = create({ schema: createIdentifier("schema") });
|
|
1287
|
+
if (options$6.database === "mysql") {
|
|
1288
|
+
const mode = options$6.mysql === "planetscale" ? "planetscale" : "default";
|
|
1289
|
+
property(paramObject, {
|
|
1290
|
+
name: "mode",
|
|
1291
|
+
fallback: createLiteral(mode)
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
drizzleCall.arguments.push(paramObject);
|
|
1295
|
+
const db = declaration(ast, {
|
|
1296
|
+
kind: "const",
|
|
1297
|
+
name: "db",
|
|
1298
|
+
value: drizzleCall
|
|
1299
|
+
});
|
|
1300
|
+
createNamed(ast, {
|
|
1301
|
+
name: "db",
|
|
1302
|
+
fallback: db
|
|
1303
|
+
});
|
|
1304
|
+
return generateCode();
|
|
1305
|
+
});
|
|
1306
|
+
},
|
|
1307
|
+
nextSteps: ({ options: options$6, highlighter, packageManager }) => {
|
|
1308
|
+
const steps = [`You will need to set ${highlighter.env("DATABASE_URL")} in your production environment`];
|
|
1309
|
+
if (options$6.docker) {
|
|
1310
|
+
const { command: command$1, args: args$1 } = resolveCommand(packageManager, "run", ["db:start"]);
|
|
1311
|
+
steps.push(`Run ${highlighter.command(`${command$1} ${args$1.join(" ")}`)} to start the docker container`);
|
|
1312
|
+
} else steps.push(`Check ${highlighter.env("DATABASE_URL")} in ${highlighter.path(".env")} and adjust it to your needs`);
|
|
1313
|
+
const { command, args } = resolveCommand(packageManager, "run", ["db:push"]);
|
|
1314
|
+
steps.push(`Run ${highlighter.command(`${command} ${args.join(" ")}`)} to update your database schema`);
|
|
1315
|
+
return steps;
|
|
1316
|
+
}
|
|
1317
|
+
});
|
|
1318
|
+
function generateEnvFileContent(content, opts) {
|
|
1319
|
+
const DB_URL_KEY = "DATABASE_URL";
|
|
1320
|
+
if (opts.docker) {
|
|
1321
|
+
const protocol = opts.database === "mysql" ? "mysql" : "postgres";
|
|
1322
|
+
const port = PORTS[opts.database];
|
|
1323
|
+
content = addEnvVar(content, DB_URL_KEY, `"${protocol}://root:mysecretpassword@localhost:${port}/local"`);
|
|
1324
|
+
return content;
|
|
1325
|
+
}
|
|
1326
|
+
if (opts.sqlite === "better-sqlite3" || opts.sqlite === "libsql") {
|
|
1327
|
+
const dbFile = opts.sqlite === "libsql" ? "file:local.db" : "local.db";
|
|
1328
|
+
content = addEnvVar(content, DB_URL_KEY, dbFile);
|
|
1329
|
+
return content;
|
|
1330
|
+
}
|
|
1331
|
+
content = addEnvComment(content, "Replace with your DB credentials!");
|
|
1332
|
+
if (opts.sqlite === "turso") {
|
|
1333
|
+
content = addEnvVar(content, DB_URL_KEY, "\"libsql://db-name-user.turso.io\"");
|
|
1334
|
+
content = addEnvVar(content, "DATABASE_AUTH_TOKEN", "\"\"");
|
|
1335
|
+
content = addEnvComment(content, "A local DB can also be used in dev as well");
|
|
1336
|
+
content = addEnvComment(content, `${DB_URL_KEY}="file:local.db"`);
|
|
1337
|
+
}
|
|
1338
|
+
if (opts.database === "mysql") content = addEnvVar(content, DB_URL_KEY, "\"mysql://user:password@host:port/db-name\"");
|
|
1339
|
+
if (opts.database === "postgresql") content = addEnvVar(content, DB_URL_KEY, "\"postgres://user:password@host:port/db-name\"");
|
|
1340
|
+
return content;
|
|
1341
|
+
}
|
|
1342
|
+
function addEnvVar(content, key, value) {
|
|
1343
|
+
if (!content.includes(key + "=")) content = appendEnvContent(content, `${key}=${value}`);
|
|
1344
|
+
return content;
|
|
1345
|
+
}
|
|
1346
|
+
function addEnvComment(content, comment) {
|
|
1347
|
+
const commented = `# ${comment}`;
|
|
1348
|
+
if (!content.includes(commented)) content = appendEnvContent(content, commented);
|
|
1349
|
+
return content;
|
|
1350
|
+
}
|
|
1351
|
+
function appendEnvContent(existing, content) {
|
|
1352
|
+
return (!existing.length || existing.endsWith("\n") ? existing : existing + "\n") + content + "\n";
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
//#endregion
|
|
1356
|
+
//#region ../addons/eslint/index.ts
|
|
1357
|
+
var eslint_default = defineAddon({
|
|
1358
|
+
id: "eslint",
|
|
1359
|
+
shortDescription: "linter",
|
|
1360
|
+
homepage: "https://eslint.org",
|
|
1361
|
+
options: {},
|
|
1362
|
+
run: ({ sv, typescript, dependencyVersion }) => {
|
|
1363
|
+
const prettierInstalled = Boolean(dependencyVersion("prettier"));
|
|
1364
|
+
sv.devDependency("eslint", "^9.36.0");
|
|
1365
|
+
sv.devDependency("@eslint/compat", "^1.4.0");
|
|
1366
|
+
sv.devDependency("eslint-plugin-svelte", "^3.12.4");
|
|
1367
|
+
sv.devDependency("globals", "^16.4.0");
|
|
1368
|
+
sv.devDependency("@eslint/js", "^9.36.0");
|
|
1369
|
+
sv.devDependency("@types/node", getNodeTypesVersion());
|
|
1370
|
+
if (typescript) sv.devDependency("typescript-eslint", "^8.44.1");
|
|
1371
|
+
if (prettierInstalled) sv.devDependency("eslint-config-prettier", "^10.1.8");
|
|
1372
|
+
sv.file("package.json", (content) => {
|
|
1373
|
+
const { data, generateCode } = parseJson(content);
|
|
1374
|
+
data.scripts ??= {};
|
|
1375
|
+
const scripts = data.scripts;
|
|
1376
|
+
const LINT_CMD = "eslint .";
|
|
1377
|
+
scripts["lint"] ??= LINT_CMD;
|
|
1378
|
+
if (!scripts["lint"].includes(LINT_CMD)) scripts["lint"] += ` && ${LINT_CMD}`;
|
|
1379
|
+
return generateCode();
|
|
1380
|
+
});
|
|
1381
|
+
sv.file(".vscode/settings.json", (content) => {
|
|
1382
|
+
if (!content) return content;
|
|
1383
|
+
const { data, generateCode } = parseJson(content);
|
|
1384
|
+
const validate = data["eslint.validate"];
|
|
1385
|
+
if (validate && !validate.includes("svelte")) validate.push("svelte");
|
|
1386
|
+
return generateCode();
|
|
1387
|
+
});
|
|
1388
|
+
sv.file("eslint.config.js", (content) => {
|
|
1389
|
+
const { ast, generateCode } = parseScript(content);
|
|
1390
|
+
const eslintConfigs = [];
|
|
1391
|
+
addDefault(ast, {
|
|
1392
|
+
from: "./svelte.config.js",
|
|
1393
|
+
as: "svelteConfig"
|
|
1394
|
+
});
|
|
1395
|
+
const gitIgnorePathStatement = parseStatement("\nconst gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url));");
|
|
1396
|
+
appendStatement(ast, { statement: gitIgnorePathStatement });
|
|
1397
|
+
const ignoresConfig = parseExpression("includeIgnoreFile(gitignorePath)");
|
|
1398
|
+
eslintConfigs.push(ignoresConfig);
|
|
1399
|
+
const jsConfig = parseExpression("js.configs.recommended");
|
|
1400
|
+
eslintConfigs.push(jsConfig);
|
|
1401
|
+
if (typescript) {
|
|
1402
|
+
const tsConfig = parseExpression("ts.configs.recommended");
|
|
1403
|
+
eslintConfigs.push(createSpread(tsConfig));
|
|
1404
|
+
}
|
|
1405
|
+
const svelteConfig = parseExpression("svelte.configs.recommended");
|
|
1406
|
+
eslintConfigs.push(createSpread(svelteConfig));
|
|
1407
|
+
const globalsBrowser = createSpread(parseExpression("globals.browser"));
|
|
1408
|
+
const globalsNode = createSpread(parseExpression("globals.node"));
|
|
1409
|
+
const globalsObjLiteral = create({});
|
|
1410
|
+
globalsObjLiteral.properties = [globalsBrowser, globalsNode];
|
|
1411
|
+
const rules = create({ "\"no-undef\"": "off" });
|
|
1412
|
+
if (rules.properties[0].type !== "Property") throw new Error("rules.properties[0].type !== \"Property\"");
|
|
1413
|
+
rules.properties[0].key.leadingComments = [{
|
|
1414
|
+
type: "Line",
|
|
1415
|
+
value: " typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects."
|
|
1416
|
+
}, {
|
|
1417
|
+
type: "Line",
|
|
1418
|
+
value: " see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors"
|
|
1419
|
+
}];
|
|
1420
|
+
const globalsConfig = create({
|
|
1421
|
+
languageOptions: { globals: globalsObjLiteral },
|
|
1422
|
+
rules: typescript ? rules : void 0
|
|
1423
|
+
});
|
|
1424
|
+
eslintConfigs.push(globalsConfig);
|
|
1425
|
+
if (typescript) {
|
|
1426
|
+
const svelteTSParserConfig = create({
|
|
1427
|
+
files: [
|
|
1428
|
+
"**/*.svelte",
|
|
1429
|
+
"**/*.svelte.ts",
|
|
1430
|
+
"**/*.svelte.js"
|
|
1431
|
+
],
|
|
1432
|
+
languageOptions: { parserOptions: {
|
|
1433
|
+
projectService: true,
|
|
1434
|
+
extraFileExtensions: [".svelte"],
|
|
1435
|
+
parser: createIdentifier("ts.parser"),
|
|
1436
|
+
svelteConfig: createIdentifier("svelteConfig")
|
|
1437
|
+
} }
|
|
1438
|
+
});
|
|
1439
|
+
eslintConfigs.push(svelteTSParserConfig);
|
|
1440
|
+
} else {
|
|
1441
|
+
const svelteTSParserConfig = create({
|
|
1442
|
+
files: ["**/*.svelte", "**/*.svelte.js"],
|
|
1443
|
+
languageOptions: { parserOptions: { svelteConfig: createIdentifier("svelteConfig") } }
|
|
1444
|
+
});
|
|
1445
|
+
eslintConfigs.push(svelteTSParserConfig);
|
|
1446
|
+
}
|
|
1447
|
+
let exportExpression;
|
|
1448
|
+
if (typescript) {
|
|
1449
|
+
const tsConfigCall = createCall({
|
|
1450
|
+
name: "defineConfig",
|
|
1451
|
+
args: []
|
|
1452
|
+
});
|
|
1453
|
+
tsConfigCall.arguments.push(...eslintConfigs);
|
|
1454
|
+
exportExpression = tsConfigCall;
|
|
1455
|
+
} else {
|
|
1456
|
+
const eslintArray = create$1();
|
|
1457
|
+
eslintConfigs.map((x$1) => append(eslintArray, x$1));
|
|
1458
|
+
exportExpression = eslintArray;
|
|
1459
|
+
}
|
|
1460
|
+
const { value: defaultExport, astNode } = createDefault(ast, { fallback: exportExpression });
|
|
1461
|
+
if (defaultExport !== exportExpression) {
|
|
1462
|
+
T.warn("An eslint config is already defined. Skipping initialization.");
|
|
1463
|
+
return content;
|
|
1464
|
+
}
|
|
1465
|
+
if (!typescript) addJsDocTypeComment(astNode, { type: "import('eslint').Linter.Config[]" });
|
|
1466
|
+
if (typescript) addDefault(ast, {
|
|
1467
|
+
from: "typescript-eslint",
|
|
1468
|
+
as: "ts"
|
|
1469
|
+
});
|
|
1470
|
+
addDefault(ast, {
|
|
1471
|
+
from: "globals",
|
|
1472
|
+
as: "globals"
|
|
1473
|
+
});
|
|
1474
|
+
if (typescript) addNamed(ast, {
|
|
1475
|
+
from: "eslint/config",
|
|
1476
|
+
imports: ["defineConfig"]
|
|
1477
|
+
});
|
|
1478
|
+
addDefault(ast, {
|
|
1479
|
+
from: "eslint-plugin-svelte",
|
|
1480
|
+
as: "svelte"
|
|
1481
|
+
});
|
|
1482
|
+
addDefault(ast, {
|
|
1483
|
+
from: "@eslint/js",
|
|
1484
|
+
as: "js"
|
|
1485
|
+
});
|
|
1486
|
+
addNamed(ast, {
|
|
1487
|
+
from: "@eslint/compat",
|
|
1488
|
+
imports: ["includeIgnoreFile"]
|
|
1489
|
+
});
|
|
1490
|
+
addNamed(ast, {
|
|
1491
|
+
from: "node:url",
|
|
1492
|
+
imports: ["fileURLToPath"]
|
|
1493
|
+
});
|
|
1494
|
+
return generateCode();
|
|
1495
|
+
});
|
|
1496
|
+
if (prettierInstalled) sv.file("eslint.config.js", addEslintConfigPrettier);
|
|
1497
|
+
}
|
|
1498
|
+
});
|
|
1499
|
+
|
|
1500
|
+
//#endregion
|
|
1501
|
+
//#region ../addons/lucia/index.ts
|
|
1502
|
+
const TABLE_TYPE = {
|
|
1503
|
+
mysql: "mysqlTable",
|
|
1504
|
+
postgresql: "pgTable",
|
|
1505
|
+
sqlite: "sqliteTable",
|
|
1506
|
+
turso: "sqliteTable"
|
|
1507
|
+
};
|
|
1508
|
+
let drizzleDialect;
|
|
1509
|
+
let schemaPath;
|
|
1510
|
+
const options$4 = defineAddonOptions().add("demo", {
|
|
1511
|
+
type: "boolean",
|
|
1512
|
+
default: true,
|
|
1513
|
+
question: `Do you want to include a demo? ${import_picocolors$2.default.dim("(includes a login/register page)")}`
|
|
1514
|
+
}).build();
|
|
1515
|
+
var lucia_default = defineAddon({
|
|
1516
|
+
id: "lucia",
|
|
1517
|
+
shortDescription: "auth guide",
|
|
1518
|
+
homepage: "https://lucia-auth.com",
|
|
1519
|
+
options: options$4,
|
|
1520
|
+
setup: ({ kit, dependencyVersion, unsupported, dependsOn, runsAfter }) => {
|
|
1521
|
+
if (!kit) unsupported("Requires SvelteKit");
|
|
1522
|
+
if (!dependencyVersion("drizzle-orm")) dependsOn("drizzle");
|
|
1523
|
+
runsAfter("tailwindcss");
|
|
1524
|
+
},
|
|
1525
|
+
run: ({ sv, typescript, options: options$6, kit, dependencyVersion }) => {
|
|
1526
|
+
const ext = typescript ? "ts" : "js";
|
|
1527
|
+
sv.devDependency("@oslojs/crypto", "^1.0.1");
|
|
1528
|
+
sv.devDependency("@oslojs/encoding", "^1.1.0");
|
|
1529
|
+
if (options$6.demo) sv.dependency("@node-rs/argon2", "^2.0.2");
|
|
1530
|
+
sv.file(`drizzle.config.${ext}`, (content) => {
|
|
1531
|
+
const { ast, generateCode } = parseScript(content);
|
|
1532
|
+
const isProp = (name, node) => node.key.type === "Identifier" && node.key.name === name;
|
|
1533
|
+
walk(ast, null, { Property(node) {
|
|
1534
|
+
if (isProp("dialect", node) && node.value.type === "Literal" && typeof node.value.value === "string") drizzleDialect = node.value.value;
|
|
1535
|
+
if (isProp("schema", node) && node.value.type === "Literal" && typeof node.value.value === "string") schemaPath = node.value.value;
|
|
1536
|
+
} });
|
|
1537
|
+
if (!drizzleDialect) throw new Error("Failed to detect DB dialect in your `drizzle.config.[js|ts]` file");
|
|
1538
|
+
if (!schemaPath) throw new Error("Failed to find schema path in your `drizzle.config.[js|ts]` file");
|
|
1539
|
+
return generateCode();
|
|
1540
|
+
});
|
|
1541
|
+
sv.file(schemaPath, (content) => {
|
|
1542
|
+
const { ast, generateCode } = parseScript(content);
|
|
1543
|
+
const createTable = (name) => createCall({
|
|
1544
|
+
name: TABLE_TYPE[drizzleDialect],
|
|
1545
|
+
args: [name]
|
|
1546
|
+
});
|
|
1547
|
+
const userDecl = declaration(ast, {
|
|
1548
|
+
kind: "const",
|
|
1549
|
+
name: "user",
|
|
1550
|
+
value: createTable("user")
|
|
1551
|
+
});
|
|
1552
|
+
const sessionDecl = declaration(ast, {
|
|
1553
|
+
kind: "const",
|
|
1554
|
+
name: "session",
|
|
1555
|
+
value: createTable("session")
|
|
1556
|
+
});
|
|
1557
|
+
const user = createNamed(ast, {
|
|
1558
|
+
name: "user",
|
|
1559
|
+
fallback: userDecl
|
|
1560
|
+
});
|
|
1561
|
+
const session = createNamed(ast, {
|
|
1562
|
+
name: "session",
|
|
1563
|
+
fallback: sessionDecl
|
|
1564
|
+
});
|
|
1565
|
+
const userTable = getCallExpression(user);
|
|
1566
|
+
const sessionTable = getCallExpression(session);
|
|
1567
|
+
if (!userTable || !sessionTable) throw new Error("failed to find call expression of `user` or `session`");
|
|
1568
|
+
if (userTable.arguments.length === 1) userTable.arguments.push(create({}));
|
|
1569
|
+
if (sessionTable.arguments.length === 1) sessionTable.arguments.push(create({}));
|
|
1570
|
+
const userAttributes = userTable.arguments[1];
|
|
1571
|
+
const sessionAttributes = sessionTable.arguments[1];
|
|
1572
|
+
if (userAttributes?.type !== "ObjectExpression" || sessionAttributes?.type !== "ObjectExpression") throw new Error("unexpected shape of `user` or `session` table definition");
|
|
1573
|
+
if (drizzleDialect === "sqlite" || drizzleDialect === "turso") {
|
|
1574
|
+
addNamed(ast, {
|
|
1575
|
+
from: "drizzle-orm/sqlite-core",
|
|
1576
|
+
imports: [
|
|
1577
|
+
"sqliteTable",
|
|
1578
|
+
"text",
|
|
1579
|
+
"integer"
|
|
1580
|
+
]
|
|
1581
|
+
});
|
|
1582
|
+
overrideProperties(userAttributes, { id: parseExpression("text('id').primaryKey()") });
|
|
1583
|
+
if (options$6.demo) overrideProperties(userAttributes, {
|
|
1584
|
+
username: parseExpression("text('username').notNull().unique()"),
|
|
1585
|
+
passwordHash: parseExpression("text('password_hash').notNull()")
|
|
1586
|
+
});
|
|
1587
|
+
overrideProperties(sessionAttributes, {
|
|
1588
|
+
id: parseExpression("text('id').primaryKey()"),
|
|
1589
|
+
userId: parseExpression("text('user_id').notNull().references(() => user.id)"),
|
|
1590
|
+
expiresAt: parseExpression("integer('expires_at', { mode: 'timestamp' }).notNull()")
|
|
1591
|
+
});
|
|
1592
|
+
}
|
|
1593
|
+
if (drizzleDialect === "mysql") {
|
|
1594
|
+
addNamed(ast, {
|
|
1595
|
+
from: "drizzle-orm/mysql-core",
|
|
1596
|
+
imports: [
|
|
1597
|
+
"mysqlTable",
|
|
1598
|
+
"varchar",
|
|
1599
|
+
"datetime"
|
|
1600
|
+
]
|
|
1601
|
+
});
|
|
1602
|
+
overrideProperties(userAttributes, { id: parseExpression("varchar('id', { length: 255 }).primaryKey()") });
|
|
1603
|
+
if (options$6.demo) overrideProperties(userAttributes, {
|
|
1604
|
+
username: parseExpression("varchar('username', { length: 32 }).notNull().unique()"),
|
|
1605
|
+
passwordHash: parseExpression("varchar('password_hash', { length: 255 }).notNull()")
|
|
1606
|
+
});
|
|
1607
|
+
overrideProperties(sessionAttributes, {
|
|
1608
|
+
id: parseExpression("varchar('id', { length: 255 }).primaryKey()"),
|
|
1609
|
+
userId: parseExpression("varchar('user_id', { length: 255 }).notNull().references(() => user.id)"),
|
|
1610
|
+
expiresAt: parseExpression("datetime('expires_at').notNull()")
|
|
1611
|
+
});
|
|
1612
|
+
}
|
|
1613
|
+
if (drizzleDialect === "postgresql") {
|
|
1614
|
+
addNamed(ast, {
|
|
1615
|
+
from: "drizzle-orm/pg-core",
|
|
1616
|
+
imports: [
|
|
1617
|
+
"pgTable",
|
|
1618
|
+
"text",
|
|
1619
|
+
"timestamp"
|
|
1620
|
+
]
|
|
1621
|
+
});
|
|
1622
|
+
overrideProperties(userAttributes, { id: parseExpression("text('id').primaryKey()") });
|
|
1623
|
+
if (options$6.demo) overrideProperties(userAttributes, {
|
|
1624
|
+
username: parseExpression("text('username').notNull().unique()"),
|
|
1625
|
+
passwordHash: parseExpression("text('password_hash').notNull()")
|
|
1626
|
+
});
|
|
1627
|
+
overrideProperties(sessionAttributes, {
|
|
1628
|
+
id: parseExpression("text('id').primaryKey()"),
|
|
1629
|
+
userId: parseExpression("text('user_id').notNull().references(() => user.id)"),
|
|
1630
|
+
expiresAt: parseExpression("timestamp('expires_at', { withTimezone: true, mode: 'date' }).notNull()")
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
let code = generateCode();
|
|
1634
|
+
if (typescript) {
|
|
1635
|
+
if (!code.includes("export type Session =")) code += "\n\nexport type Session = typeof session.$inferSelect;";
|
|
1636
|
+
if (!code.includes("export type User =")) code += "\n\nexport type User = typeof user.$inferSelect;";
|
|
1637
|
+
}
|
|
1638
|
+
return code;
|
|
1639
|
+
});
|
|
1640
|
+
sv.file(`${kit?.libDirectory}/server/auth.${ext}`, (content) => {
|
|
1641
|
+
const { ast, generateCode } = parseScript(content);
|
|
1642
|
+
addNamespace(ast, {
|
|
1643
|
+
from: "$lib/server/db/schema",
|
|
1644
|
+
as: "table"
|
|
1645
|
+
});
|
|
1646
|
+
addNamed(ast, {
|
|
1647
|
+
from: "$lib/server/db",
|
|
1648
|
+
imports: ["db"]
|
|
1649
|
+
});
|
|
1650
|
+
addNamed(ast, {
|
|
1651
|
+
from: "@oslojs/encoding",
|
|
1652
|
+
imports: ["encodeBase64url", "encodeHexLowerCase"]
|
|
1653
|
+
});
|
|
1654
|
+
addNamed(ast, {
|
|
1655
|
+
from: "@oslojs/crypto/sha2",
|
|
1656
|
+
imports: ["sha256"]
|
|
1657
|
+
});
|
|
1658
|
+
addNamed(ast, {
|
|
1659
|
+
from: "drizzle-orm",
|
|
1660
|
+
imports: ["eq"]
|
|
1661
|
+
});
|
|
1662
|
+
if (typescript) addNamed(ast, {
|
|
1663
|
+
from: "@sveltejs/kit",
|
|
1664
|
+
imports: ["RequestEvent"],
|
|
1665
|
+
isType: true
|
|
1666
|
+
});
|
|
1667
|
+
const ms = new MagicString(generateCode().trim());
|
|
1668
|
+
const [ts] = createPrinter(typescript);
|
|
1669
|
+
if (!ms.original.includes("const DAY_IN_MS")) ms.append("\n\nconst DAY_IN_MS = 1000 * 60 * 60 * 24;");
|
|
1670
|
+
if (!ms.original.includes("export const sessionCookieName")) ms.append("\n\nexport const sessionCookieName = 'auth-session';");
|
|
1671
|
+
if (!ms.original.includes("export function generateSessionToken")) {
|
|
1672
|
+
const generateSessionToken = dedent_default`
|
|
1673
|
+
export function generateSessionToken() {
|
|
1674
|
+
const bytes = crypto.getRandomValues(new Uint8Array(18));
|
|
1675
|
+
const token = encodeBase64url(bytes);
|
|
1676
|
+
return token;
|
|
1677
|
+
}`;
|
|
1678
|
+
ms.append(`\n\n${generateSessionToken}`);
|
|
1679
|
+
}
|
|
1680
|
+
if (!ms.original.includes("async function createSession")) {
|
|
1681
|
+
const createSession = dedent_default`
|
|
1682
|
+
${ts("", "/**")}
|
|
1683
|
+
${ts("", " * @param {string} token")}
|
|
1684
|
+
${ts("", " * @param {string} userId")}
|
|
1685
|
+
${ts("", " */")}
|
|
1686
|
+
export async function createSession(token${ts(": string")}, userId${ts(": string")}) {
|
|
1687
|
+
const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
|
|
1688
|
+
const session${ts(": table.Session")} = {
|
|
1689
|
+
id: sessionId,
|
|
1690
|
+
userId,
|
|
1691
|
+
expiresAt: new Date(Date.now() + DAY_IN_MS * 30)
|
|
1692
|
+
};
|
|
1693
|
+
await db.insert(table.session).values(session);
|
|
1694
|
+
return session;
|
|
1695
|
+
}`;
|
|
1696
|
+
ms.append(`\n\n${createSession}`);
|
|
1697
|
+
}
|
|
1698
|
+
if (!ms.original.includes("async function validateSessionToken")) {
|
|
1699
|
+
const validateSessionToken = dedent_default`
|
|
1700
|
+
${ts("", "/** @param {string} token */")}
|
|
1701
|
+
export async function validateSessionToken(token${ts(": string")}) {
|
|
1702
|
+
const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
|
|
1703
|
+
const [result] = await db
|
|
1704
|
+
.select({
|
|
1705
|
+
// Adjust user table here to tweak returned data
|
|
1706
|
+
user: { id: table.user.id, username: table.user.username },
|
|
1707
|
+
session: table.session
|
|
1708
|
+
})
|
|
1709
|
+
.from(table.session)
|
|
1710
|
+
.innerJoin(table.user, eq(table.session.userId, table.user.id))
|
|
1711
|
+
.where(eq(table.session.id, sessionId));
|
|
1712
|
+
|
|
1713
|
+
if (!result) {
|
|
1714
|
+
return { session: null, user: null };
|
|
1715
|
+
}
|
|
1716
|
+
const { session, user } = result;
|
|
1717
|
+
|
|
1718
|
+
const sessionExpired = Date.now() >= session.expiresAt.getTime();
|
|
1719
|
+
if (sessionExpired) {
|
|
1720
|
+
await db.delete(table.session).where(eq(table.session.id, session.id));
|
|
1721
|
+
return { session: null, user: null };
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
const renewSession = Date.now() >= session.expiresAt.getTime() - DAY_IN_MS * 15;
|
|
1725
|
+
if (renewSession) {
|
|
1726
|
+
session.expiresAt = new Date(Date.now() + DAY_IN_MS * 30);
|
|
1727
|
+
await db
|
|
1728
|
+
.update(table.session)
|
|
1729
|
+
.set({ expiresAt: session.expiresAt })
|
|
1730
|
+
.where(eq(table.session.id, session.id));
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
return { session, user };
|
|
1734
|
+
}`;
|
|
1735
|
+
ms.append(`\n\n${validateSessionToken}`);
|
|
1736
|
+
}
|
|
1737
|
+
if (typescript && !ms.original.includes("export type SessionValidationResult")) ms.append(`\n\nexport type SessionValidationResult = Awaited<ReturnType<typeof validateSessionToken>>;`);
|
|
1738
|
+
if (!ms.original.includes("async function invalidateSession")) {
|
|
1739
|
+
const invalidateSession = dedent_default`
|
|
1740
|
+
${ts("", "/** @param {string} sessionId */")}
|
|
1741
|
+
export async function invalidateSession(sessionId${ts(": string")}) {
|
|
1742
|
+
await db.delete(table.session).where(eq(table.session.id, sessionId));
|
|
1743
|
+
}`;
|
|
1744
|
+
ms.append(`\n\n${invalidateSession}`);
|
|
1745
|
+
}
|
|
1746
|
+
if (!ms.original.includes("export function setSessionTokenCookie")) {
|
|
1747
|
+
const setSessionTokenCookie = dedent_default`
|
|
1748
|
+
${ts("", "/**")}
|
|
1749
|
+
${ts("", " * @param {import(\"@sveltejs/kit\").RequestEvent} event")}
|
|
1750
|
+
${ts("", " * @param {string} token")}
|
|
1751
|
+
${ts("", " * @param {Date} expiresAt")}
|
|
1752
|
+
${ts("", " */")}
|
|
1753
|
+
export function setSessionTokenCookie(event${ts(": RequestEvent")}, token${ts(": string")}, expiresAt${ts(": Date")}) {
|
|
1754
|
+
event.cookies.set(sessionCookieName, token, {
|
|
1755
|
+
expires: expiresAt,
|
|
1756
|
+
path: '/'
|
|
1757
|
+
});
|
|
1758
|
+
}`;
|
|
1759
|
+
ms.append(`\n\n${setSessionTokenCookie}`);
|
|
1760
|
+
}
|
|
1761
|
+
if (!ms.original.includes("export function deleteSessionTokenCookie")) {
|
|
1762
|
+
const deleteSessionTokenCookie = dedent_default`
|
|
1763
|
+
${ts("", "/** @param {import(\"@sveltejs/kit\").RequestEvent} event */")}
|
|
1764
|
+
export function deleteSessionTokenCookie(event${ts(": RequestEvent")}) {
|
|
1765
|
+
event.cookies.delete(sessionCookieName, {
|
|
1766
|
+
path: '/'
|
|
1767
|
+
});
|
|
1768
|
+
}`;
|
|
1769
|
+
ms.append(`\n\n${deleteSessionTokenCookie}`);
|
|
1770
|
+
}
|
|
1771
|
+
return ms.toString();
|
|
1772
|
+
});
|
|
1773
|
+
if (typescript) sv.file("src/app.d.ts", (content) => {
|
|
1774
|
+
const { ast, generateCode } = parseScript(content);
|
|
1775
|
+
const locals = addGlobalAppInterface(ast, { name: "Locals" });
|
|
1776
|
+
if (!locals) throw new Error("Failed detecting `locals` interface in `src/app.d.ts`");
|
|
1777
|
+
const user = locals.body.body.find((prop) => hasTypeProperty(prop, { name: "user" }));
|
|
1778
|
+
const session = locals.body.body.find((prop) => hasTypeProperty(prop, { name: "session" }));
|
|
1779
|
+
if (!user) locals.body.body.push(createLuciaType("user"));
|
|
1780
|
+
if (!session) locals.body.body.push(createLuciaType("session"));
|
|
1781
|
+
return generateCode();
|
|
1782
|
+
});
|
|
1783
|
+
sv.file(`src/hooks.server.${ext}`, (content) => {
|
|
1784
|
+
const { ast, generateCode } = parseScript(content);
|
|
1785
|
+
addNamespace(ast, {
|
|
1786
|
+
from: "$lib/server/auth",
|
|
1787
|
+
as: "auth"
|
|
1788
|
+
});
|
|
1789
|
+
addHooksHandle(ast, {
|
|
1790
|
+
typescript,
|
|
1791
|
+
newHandleName: "handleAuth",
|
|
1792
|
+
handleContent: getAuthHandleContent()
|
|
1793
|
+
});
|
|
1794
|
+
return generateCode();
|
|
1795
|
+
});
|
|
1796
|
+
if (options$6.demo) {
|
|
1797
|
+
sv.file(`${kit?.routesDirectory}/demo/+page.svelte`, (content) => {
|
|
1798
|
+
return addToDemoPage(content, "lucia");
|
|
1799
|
+
});
|
|
1800
|
+
sv.file(`${kit.routesDirectory}/demo/lucia/login/+page.server.${ext}`, (content) => {
|
|
1801
|
+
if (content) {
|
|
1802
|
+
const filePath = `${kit.routesDirectory}/demo/lucia/login/+page.server.${typescript ? "ts" : "js"}`;
|
|
1803
|
+
T.warn(`Existing ${import_picocolors$2.default.yellow(filePath)} file. Could not update.`);
|
|
1804
|
+
return content;
|
|
1805
|
+
}
|
|
1806
|
+
const [ts] = createPrinter(typescript);
|
|
1807
|
+
return dedent_default`
|
|
1808
|
+
import { hash, verify } from '@node-rs/argon2';
|
|
1809
|
+
import { encodeBase32LowerCase } from '@oslojs/encoding';
|
|
1810
|
+
import { fail, redirect } from '@sveltejs/kit';
|
|
1811
|
+
import { eq } from 'drizzle-orm';
|
|
1812
|
+
import * as auth from '$lib/server/auth';
|
|
1813
|
+
import { db } from '$lib/server/db';
|
|
1814
|
+
import * as table from '$lib/server/db/schema';
|
|
1815
|
+
${ts("import type { Actions, PageServerLoad } from './$types';\n")}
|
|
1816
|
+
export const load${ts(": PageServerLoad")} = async (event) => {
|
|
1817
|
+
if (event.locals.user) {
|
|
1818
|
+
return redirect(302, '/demo/lucia');
|
|
1819
|
+
}
|
|
1820
|
+
return {};
|
|
1821
|
+
};
|
|
1822
|
+
|
|
1823
|
+
export const actions${ts(": Actions")} = {
|
|
1824
|
+
login: async (event) => {
|
|
1825
|
+
const formData = await event.request.formData();
|
|
1826
|
+
const username = formData.get('username');
|
|
1827
|
+
const password = formData.get('password');
|
|
1828
|
+
|
|
1829
|
+
if (!validateUsername(username)) {
|
|
1830
|
+
return fail(400, { message: 'Invalid username (min 3, max 31 characters, alphanumeric only)' });
|
|
1831
|
+
}
|
|
1832
|
+
if (!validatePassword(password)) {
|
|
1833
|
+
return fail(400, { message: 'Invalid password (min 6, max 255 characters)' });
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
const results = await db
|
|
1837
|
+
.select()
|
|
1838
|
+
.from(table.user)
|
|
1839
|
+
.where(eq(table.user.username, username));
|
|
1840
|
+
|
|
1841
|
+
const existingUser = results.at(0);
|
|
1842
|
+
if (!existingUser) {
|
|
1843
|
+
return fail(400, { message: 'Incorrect username or password' });
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
const validPassword = await verify(existingUser.passwordHash, password, {
|
|
1847
|
+
memoryCost: 19456,
|
|
1848
|
+
timeCost: 2,
|
|
1849
|
+
outputLen: 32,
|
|
1850
|
+
parallelism: 1,
|
|
1851
|
+
});
|
|
1852
|
+
if (!validPassword) {
|
|
1853
|
+
return fail(400, { message: 'Incorrect username or password' });
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
const sessionToken = auth.generateSessionToken();
|
|
1857
|
+
const session = await auth.createSession(sessionToken, existingUser.id);
|
|
1858
|
+
auth.setSessionTokenCookie(event, sessionToken, session.expiresAt);
|
|
1859
|
+
|
|
1860
|
+
return redirect(302, '/demo/lucia');
|
|
1861
|
+
},
|
|
1862
|
+
register: async (event) => {
|
|
1863
|
+
const formData = await event.request.formData();
|
|
1864
|
+
const username = formData.get('username');
|
|
1865
|
+
const password = formData.get('password');
|
|
1866
|
+
|
|
1867
|
+
if (!validateUsername(username)) {
|
|
1868
|
+
return fail(400, { message: 'Invalid username' });
|
|
1869
|
+
}
|
|
1870
|
+
if (!validatePassword(password)) {
|
|
1871
|
+
return fail(400, { message: 'Invalid password' });
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
const userId = generateUserId();
|
|
1875
|
+
const passwordHash = await hash(password, {
|
|
1876
|
+
// recommended minimum parameters
|
|
1877
|
+
memoryCost: 19456,
|
|
1878
|
+
timeCost: 2,
|
|
1879
|
+
outputLen: 32,
|
|
1880
|
+
parallelism: 1,
|
|
1881
|
+
});
|
|
1882
|
+
|
|
1883
|
+
try {
|
|
1884
|
+
await db.insert(table.user).values({ id: userId, username, passwordHash });
|
|
1885
|
+
|
|
1886
|
+
const sessionToken = auth.generateSessionToken();
|
|
1887
|
+
const session = await auth.createSession(sessionToken, userId);
|
|
1888
|
+
auth.setSessionTokenCookie(event, sessionToken, session.expiresAt);
|
|
1889
|
+
} catch {
|
|
1890
|
+
return fail(500, { message: 'An error has occurred' });
|
|
1891
|
+
}
|
|
1892
|
+
return redirect(302, '/demo/lucia');
|
|
1893
|
+
},
|
|
1894
|
+
};
|
|
1895
|
+
|
|
1896
|
+
function generateUserId() {
|
|
1897
|
+
// ID with 120 bits of entropy, or about the same as UUID v4.
|
|
1898
|
+
const bytes = crypto.getRandomValues(new Uint8Array(15));
|
|
1899
|
+
const id = encodeBase32LowerCase(bytes);
|
|
1900
|
+
return id;
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
function validateUsername(username${ts(": unknown")})${ts(": username is string")} {
|
|
1904
|
+
return (
|
|
1905
|
+
typeof username === 'string' &&
|
|
1906
|
+
username.length >= 3 &&
|
|
1907
|
+
username.length <= 31 &&
|
|
1908
|
+
/^[a-z0-9_-]+$/.test(username)
|
|
1909
|
+
);
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
function validatePassword(password${ts(": unknown")})${ts(": password is string")} {
|
|
1913
|
+
return (
|
|
1914
|
+
typeof password === 'string' &&
|
|
1915
|
+
password.length >= 6 &&
|
|
1916
|
+
password.length <= 255
|
|
1917
|
+
);
|
|
1918
|
+
}
|
|
1919
|
+
`;
|
|
1920
|
+
});
|
|
1921
|
+
sv.file(`${kit.routesDirectory}/demo/lucia/login/+page.svelte`, (content) => {
|
|
1922
|
+
if (content) {
|
|
1923
|
+
const filePath = `${kit.routesDirectory}/demo/lucia/login/+page.svelte`;
|
|
1924
|
+
T.warn(`Existing ${import_picocolors$2.default.yellow(filePath)} file. Could not update.`);
|
|
1925
|
+
return content;
|
|
1926
|
+
}
|
|
1927
|
+
const tailwind = dependencyVersion("@tailwindcss/vite") !== void 0;
|
|
1928
|
+
const twInputClasses = "class=\"mt-1 px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500\"";
|
|
1929
|
+
const twBtnClasses = "class=\"bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition\"";
|
|
1930
|
+
const svelte5 = !!dependencyVersion("svelte")?.startsWith("5");
|
|
1931
|
+
const [ts, s5] = createPrinter(typescript, svelte5);
|
|
1932
|
+
return dedent_default`
|
|
1933
|
+
<script ${ts("lang='ts'")}>
|
|
1934
|
+
import { enhance } from '$app/forms';
|
|
1935
|
+
${ts("import type { ActionData } from './$types';\n")}
|
|
1936
|
+
${s5(`let { form }${ts(": { form: ActionData }")} = $props();`, `export let form${ts(": ActionData")};`)}
|
|
1937
|
+
<\/script>
|
|
1938
|
+
|
|
1939
|
+
<h1>Login/Register</h1>
|
|
1940
|
+
<form method="post" action="?/login" use:enhance>
|
|
1941
|
+
<label>
|
|
1942
|
+
Username
|
|
1943
|
+
<input
|
|
1944
|
+
name="username"
|
|
1945
|
+
${tailwind ? twInputClasses : ""}
|
|
1946
|
+
/>
|
|
1947
|
+
</label>
|
|
1948
|
+
<label>
|
|
1949
|
+
Password
|
|
1950
|
+
<input
|
|
1951
|
+
type="password"
|
|
1952
|
+
name="password"
|
|
1953
|
+
${tailwind ? twInputClasses : ""}
|
|
1954
|
+
/>
|
|
1955
|
+
</label>
|
|
1956
|
+
<button ${tailwind ? twBtnClasses : ""}
|
|
1957
|
+
>Login</button>
|
|
1958
|
+
<button
|
|
1959
|
+
formaction="?/register"
|
|
1960
|
+
${tailwind ? twBtnClasses : ""}
|
|
1961
|
+
>Register</button>
|
|
1962
|
+
</form>
|
|
1963
|
+
<p style='color: red'>{form?.message ?? ''}</p>
|
|
1964
|
+
`;
|
|
1965
|
+
});
|
|
1966
|
+
sv.file(`${kit.routesDirectory}/demo/lucia/+page.server.${ext}`, (content) => {
|
|
1967
|
+
if (content) {
|
|
1968
|
+
const filePath = `${kit.routesDirectory}/demo/lucia/+page.server.${typescript ? "ts" : "js"}`;
|
|
1969
|
+
T.warn(`Existing ${import_picocolors$2.default.yellow(filePath)} file. Could not update.`);
|
|
1970
|
+
return content;
|
|
1971
|
+
}
|
|
1972
|
+
const [ts] = createPrinter(typescript);
|
|
1973
|
+
return dedent_default`
|
|
1974
|
+
import * as auth from '$lib/server/auth';
|
|
1975
|
+
import { fail, redirect } from '@sveltejs/kit';
|
|
1976
|
+
import { getRequestEvent } from '$app/server';
|
|
1977
|
+
${ts("import type { Actions, PageServerLoad } from './$types';\n")}
|
|
1978
|
+
export const load${ts(": PageServerLoad")} = async () => {
|
|
1979
|
+
const user = requireLogin()
|
|
1980
|
+
return { user };
|
|
1981
|
+
};
|
|
1982
|
+
|
|
1983
|
+
export const actions${ts(": Actions")} = {
|
|
1984
|
+
logout: async (event) => {
|
|
1985
|
+
if (!event.locals.session) {
|
|
1986
|
+
return fail(401);
|
|
1987
|
+
}
|
|
1988
|
+
await auth.invalidateSession(event.locals.session.id);
|
|
1989
|
+
auth.deleteSessionTokenCookie(event);
|
|
1990
|
+
|
|
1991
|
+
return redirect(302, '/demo/lucia/login');
|
|
1992
|
+
},
|
|
1993
|
+
};
|
|
1994
|
+
|
|
1995
|
+
function requireLogin() {
|
|
1996
|
+
const { locals } = getRequestEvent();
|
|
1997
|
+
|
|
1998
|
+
if (!locals.user) {
|
|
1999
|
+
return redirect(302, "/demo/lucia/login");
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
return locals.user;
|
|
2003
|
+
}
|
|
2004
|
+
`;
|
|
2005
|
+
});
|
|
2006
|
+
sv.file(`${kit.routesDirectory}/demo/lucia/+page.svelte`, (content) => {
|
|
2007
|
+
if (content) {
|
|
2008
|
+
const filePath = `${kit.routesDirectory}/demo/lucia/+page.svelte`;
|
|
2009
|
+
T.warn(`Existing ${import_picocolors$2.default.yellow(filePath)} file. Could not update.`);
|
|
2010
|
+
return content;
|
|
2011
|
+
}
|
|
2012
|
+
const svelte5 = !!dependencyVersion("svelte")?.startsWith("5");
|
|
2013
|
+
const [ts, s5] = createPrinter(typescript, svelte5);
|
|
2014
|
+
return dedent_default`
|
|
2015
|
+
<script ${ts("lang='ts'")}>
|
|
2016
|
+
import { enhance } from '$app/forms';
|
|
2017
|
+
${ts("import type { PageServerData } from './$types';\n")}
|
|
2018
|
+
${s5(`let { data }${ts(": { data: PageServerData }")} = $props();`, `export let data${ts(": PageServerData")};`)}
|
|
2019
|
+
<\/script>
|
|
2020
|
+
|
|
2021
|
+
<h1>Hi, {data.user.username}!</h1>
|
|
2022
|
+
<p>Your user ID is {data.user.id}.</p>
|
|
2023
|
+
<form method='post' action='?/logout' use:enhance>
|
|
2024
|
+
<button>Sign out</button>
|
|
2025
|
+
</form>
|
|
2026
|
+
`;
|
|
2027
|
+
});
|
|
2028
|
+
}
|
|
2029
|
+
},
|
|
2030
|
+
nextSteps: ({ highlighter, options: options$6, packageManager }) => {
|
|
2031
|
+
const { command, args } = resolveCommand(packageManager, "run", ["db:push"]);
|
|
2032
|
+
const steps = [`Run ${highlighter.command(`${command} ${args.join(" ")}`)} to update your database schema`];
|
|
2033
|
+
if (options$6.demo) steps.push(`Visit ${highlighter.route("/demo/lucia")} route to view the demo`);
|
|
2034
|
+
return steps;
|
|
2035
|
+
}
|
|
2036
|
+
});
|
|
2037
|
+
function createLuciaType(name) {
|
|
2038
|
+
return {
|
|
2039
|
+
type: "TSPropertySignature",
|
|
2040
|
+
key: {
|
|
2041
|
+
type: "Identifier",
|
|
2042
|
+
name
|
|
2043
|
+
},
|
|
2044
|
+
computed: false,
|
|
2045
|
+
typeAnnotation: {
|
|
2046
|
+
type: "TSTypeAnnotation",
|
|
2047
|
+
typeAnnotation: {
|
|
2048
|
+
type: "TSIndexedAccessType",
|
|
2049
|
+
objectType: {
|
|
2050
|
+
type: "TSImportType",
|
|
2051
|
+
argument: {
|
|
2052
|
+
type: "Literal",
|
|
2053
|
+
value: "$lib/server/auth"
|
|
2054
|
+
},
|
|
2055
|
+
qualifier: {
|
|
2056
|
+
type: "Identifier",
|
|
2057
|
+
name: "SessionValidationResult"
|
|
2058
|
+
}
|
|
2059
|
+
},
|
|
2060
|
+
indexType: {
|
|
2061
|
+
type: "TSLiteralType",
|
|
2062
|
+
literal: {
|
|
2063
|
+
type: "Literal",
|
|
2064
|
+
value: name
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
};
|
|
2070
|
+
}
|
|
2071
|
+
function getAuthHandleContent() {
|
|
2072
|
+
return `
|
|
2073
|
+
async ({ event, resolve }) => {
|
|
2074
|
+
const sessionToken = event.cookies.get(auth.sessionCookieName);
|
|
2075
|
+
if (!sessionToken) {
|
|
2076
|
+
event.locals.user = null;
|
|
2077
|
+
event.locals.session = null;
|
|
2078
|
+
return resolve(event);
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
const { session, user } = await auth.validateSessionToken(sessionToken);
|
|
2082
|
+
if (session) {
|
|
2083
|
+
auth.setSessionTokenCookie(event, sessionToken, session.expiresAt);
|
|
2084
|
+
} else {
|
|
2085
|
+
auth.deleteSessionTokenCookie(event);
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
event.locals.user = user;
|
|
2089
|
+
event.locals.session = session;
|
|
2090
|
+
|
|
2091
|
+
return resolve(event);
|
|
2092
|
+
};`;
|
|
2093
|
+
}
|
|
2094
|
+
function getCallExpression(ast) {
|
|
2095
|
+
let callExpression;
|
|
2096
|
+
walk(ast, null, { CallExpression(node) {
|
|
2097
|
+
callExpression ??= node;
|
|
2098
|
+
} });
|
|
2099
|
+
return callExpression;
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
//#endregion
|
|
2103
|
+
//#region ../addons/mdsvex/index.ts
|
|
2104
|
+
var mdsvex_default = defineAddon({
|
|
2105
|
+
id: "mdsvex",
|
|
2106
|
+
shortDescription: "svelte + markdown",
|
|
2107
|
+
homepage: "https://mdsvex.pngwn.io",
|
|
2108
|
+
options: {},
|
|
2109
|
+
run: ({ sv }) => {
|
|
2110
|
+
sv.devDependency("mdsvex", "^0.12.6");
|
|
2111
|
+
sv.file("svelte.config.js", (content) => {
|
|
2112
|
+
const { ast, generateCode } = parseScript(content);
|
|
2113
|
+
addNamed(ast, {
|
|
2114
|
+
from: "mdsvex",
|
|
2115
|
+
imports: ["mdsvex"]
|
|
2116
|
+
});
|
|
2117
|
+
const { value: exportDefault } = createDefault(ast, { fallback: create({}) });
|
|
2118
|
+
let preprocessorArray = property(exportDefault, {
|
|
2119
|
+
name: "preprocess",
|
|
2120
|
+
fallback: create$1()
|
|
2121
|
+
});
|
|
2122
|
+
if (!(preprocessorArray.type === "ArrayExpression")) {
|
|
2123
|
+
const previousElement = preprocessorArray;
|
|
2124
|
+
preprocessorArray = create$1();
|
|
2125
|
+
append(preprocessorArray, previousElement);
|
|
2126
|
+
overrideProperties(exportDefault, { preprocess: preprocessorArray });
|
|
2127
|
+
}
|
|
2128
|
+
const mdsvexCall = createCall({
|
|
2129
|
+
name: "mdsvex",
|
|
2130
|
+
args: []
|
|
2131
|
+
});
|
|
2132
|
+
append(preprocessorArray, mdsvexCall);
|
|
2133
|
+
const extensionsArray = property(exportDefault, {
|
|
2134
|
+
name: "extensions",
|
|
2135
|
+
fallback: create$1()
|
|
2136
|
+
});
|
|
2137
|
+
append(extensionsArray, ".svelte");
|
|
2138
|
+
append(extensionsArray, ".svx");
|
|
2139
|
+
return generateCode();
|
|
2140
|
+
});
|
|
2141
|
+
}
|
|
2142
|
+
});
|
|
2143
|
+
|
|
2144
|
+
//#endregion
|
|
2145
|
+
//#region ../core/tooling/html/index.ts
|
|
2146
|
+
function createElement(tagName, attributes = {}) {
|
|
2147
|
+
const element = new Element(tagName, {}, void 0, Tag);
|
|
2148
|
+
element.attribs = attributes;
|
|
2149
|
+
return element;
|
|
2150
|
+
}
|
|
2151
|
+
function appendElement(childNodes, elementToAppend) {
|
|
2152
|
+
childNodes.push(elementToAppend);
|
|
2153
|
+
}
|
|
2154
|
+
function addFromRawHtml(childNodes, html) {
|
|
2155
|
+
const document = parseHtml$1(html);
|
|
2156
|
+
for (const childNode of document.childNodes) childNodes.push(childNode);
|
|
2157
|
+
}
|
|
2158
|
+
function addSlot(jsAst, options$6) {
|
|
2159
|
+
if (options$6.svelteVersion && (options$6.svelteVersion.startsWith("4") || options$6.svelteVersion.startsWith("3"))) {
|
|
2160
|
+
const slot = createElement("slot");
|
|
2161
|
+
appendElement(options$6.htmlAst.childNodes, slot);
|
|
2162
|
+
return;
|
|
2163
|
+
}
|
|
2164
|
+
appendFromString(jsAst, { code: "let { children } = $props();" });
|
|
2165
|
+
addFromRawHtml(options$6.htmlAst.childNodes, "{@render children()}");
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
//#endregion
|
|
2169
|
+
//#region ../addons/paraglide/index.ts
|
|
2170
|
+
const DEFAULT_INLANG_PROJECT = {
|
|
2171
|
+
$schema: "https://inlang.com/schema/project-settings",
|
|
2172
|
+
modules: ["https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js", "https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js"],
|
|
2173
|
+
"plugin.inlang.messageFormat": { pathPattern: "./messages/{locale}.json" }
|
|
2174
|
+
};
|
|
2175
|
+
const options$3 = defineAddonOptions().add("languageTags", {
|
|
2176
|
+
question: `Which languages would you like to support? ${import_picocolors$2.default.gray("(e.g. en,de-ch)")}`,
|
|
2177
|
+
type: "string",
|
|
2178
|
+
default: "en, es",
|
|
2179
|
+
validate(input) {
|
|
2180
|
+
if (!input) return;
|
|
2181
|
+
const { invalidLanguageTags, validLanguageTags } = parseLanguageTagInput(input);
|
|
2182
|
+
if (invalidLanguageTags.length > 0) if (invalidLanguageTags.length === 1) return `The input "${invalidLanguageTags[0]}" is not a valid IETF BCP 47 language tag`;
|
|
2183
|
+
else return `The inputs ${new Intl.ListFormat("en", {
|
|
2184
|
+
style: "long",
|
|
2185
|
+
type: "conjunction"
|
|
2186
|
+
}).format(invalidLanguageTags.map((x$1) => `"${x$1}"`))} are not valid BCP47 language tags`;
|
|
2187
|
+
if (validLanguageTags.length === 0) return "Please enter at least one valid BCP47 language tag. Eg: en";
|
|
2188
|
+
}
|
|
2189
|
+
}).add("demo", {
|
|
2190
|
+
type: "boolean",
|
|
2191
|
+
default: true,
|
|
2192
|
+
question: "Do you want to include a demo?"
|
|
2193
|
+
}).build();
|
|
2194
|
+
var paraglide_default = defineAddon({
|
|
2195
|
+
id: "paraglide",
|
|
2196
|
+
shortDescription: "i18n",
|
|
2197
|
+
homepage: "https://inlang.com/m/gerre34r/library-inlang-paraglideJs",
|
|
2198
|
+
options: options$3,
|
|
2199
|
+
setup: ({ kit, unsupported }) => {
|
|
2200
|
+
if (!kit) unsupported("Requires SvelteKit");
|
|
2201
|
+
},
|
|
2202
|
+
run: ({ sv, options: options$6, viteConfigFile, typescript, kit }) => {
|
|
2203
|
+
const ext = typescript ? "ts" : "js";
|
|
2204
|
+
if (!kit) throw new Error("SvelteKit is required");
|
|
2205
|
+
const paraglideOutDir = "src/lib/paraglide";
|
|
2206
|
+
sv.devDependency("@inlang/paraglide-js", "^2.3.2");
|
|
2207
|
+
sv.file("project.inlang/settings.json", (content) => {
|
|
2208
|
+
if (content) return content;
|
|
2209
|
+
const { data, generateCode } = parseJson(content);
|
|
2210
|
+
for (const key in DEFAULT_INLANG_PROJECT) data[key] = DEFAULT_INLANG_PROJECT[key];
|
|
2211
|
+
const { validLanguageTags: validLanguageTags$1 } = parseLanguageTagInput(options$6.languageTags);
|
|
2212
|
+
data.baseLocale = validLanguageTags$1[0];
|
|
2213
|
+
data.locales = validLanguageTags$1;
|
|
2214
|
+
return generateCode();
|
|
2215
|
+
});
|
|
2216
|
+
sv.file(viteConfigFile, (content) => {
|
|
2217
|
+
const { ast, generateCode } = parseScript(content);
|
|
2218
|
+
const vitePluginName = "paraglideVitePlugin";
|
|
2219
|
+
addNamed(ast, {
|
|
2220
|
+
imports: [vitePluginName],
|
|
2221
|
+
from: "@inlang/paraglide-js"
|
|
2222
|
+
});
|
|
2223
|
+
addPlugin(ast, { code: `${vitePluginName}({
|
|
2224
|
+
project: './project.inlang',
|
|
2225
|
+
outdir: './${paraglideOutDir}'
|
|
2226
|
+
})` });
|
|
2227
|
+
return generateCode();
|
|
2228
|
+
});
|
|
2229
|
+
sv.file(`src/hooks.${ext}`, (content) => {
|
|
2230
|
+
const { ast, generateCode } = parseScript(content);
|
|
2231
|
+
addNamed(ast, {
|
|
2232
|
+
from: "$lib/paraglide/runtime",
|
|
2233
|
+
imports: ["deLocalizeUrl"]
|
|
2234
|
+
});
|
|
2235
|
+
const expression = parseExpression("(request) => deLocalizeUrl(request.url).pathname");
|
|
2236
|
+
const rerouteIdentifier = declaration(ast, {
|
|
2237
|
+
kind: "const",
|
|
2238
|
+
name: "reroute",
|
|
2239
|
+
value: expression
|
|
2240
|
+
});
|
|
2241
|
+
if (createNamed(ast, {
|
|
2242
|
+
name: "reroute",
|
|
2243
|
+
fallback: rerouteIdentifier
|
|
2244
|
+
}).declaration !== rerouteIdentifier) T.warn("Adding the reroute hook automatically failed. Add it manually");
|
|
2245
|
+
return generateCode();
|
|
2246
|
+
});
|
|
2247
|
+
sv.file(`src/hooks.server.${ext}`, (content) => {
|
|
2248
|
+
const { ast, generateCode } = parseScript(content);
|
|
2249
|
+
addNamed(ast, {
|
|
2250
|
+
from: "$lib/paraglide/server",
|
|
2251
|
+
imports: ["paraglideMiddleware"]
|
|
2252
|
+
});
|
|
2253
|
+
addHooksHandle(ast, {
|
|
2254
|
+
typescript,
|
|
2255
|
+
newHandleName: "handleParaglide",
|
|
2256
|
+
handleContent: `({ event, resolve }) => paraglideMiddleware(event.request, ({ request, locale }) => {
|
|
2257
|
+
event.request = request;
|
|
2258
|
+
return resolve(event, {
|
|
2259
|
+
transformPageChunk: ({ html }) => html.replace('%paraglide.lang%', locale)
|
|
2260
|
+
});
|
|
2261
|
+
});`
|
|
2262
|
+
});
|
|
2263
|
+
return generateCode();
|
|
2264
|
+
});
|
|
2265
|
+
sv.file("src/app.html", (content) => {
|
|
2266
|
+
const { ast, generateCode } = parseHtml(content);
|
|
2267
|
+
const htmlNode = ast.children.find((child) => child.type === Tag && child.name === "html");
|
|
2268
|
+
if (!htmlNode) {
|
|
2269
|
+
T.warn("Could not find <html> node in app.html. You'll need to add the language placeholder manually");
|
|
2270
|
+
return generateCode();
|
|
2271
|
+
}
|
|
2272
|
+
htmlNode.attribs = {
|
|
2273
|
+
...htmlNode.attribs,
|
|
2274
|
+
lang: "%paraglide.lang%"
|
|
2275
|
+
};
|
|
2276
|
+
return generateCode();
|
|
2277
|
+
});
|
|
2278
|
+
sv.file(".gitignore", (content) => {
|
|
2279
|
+
if (!content) return content;
|
|
2280
|
+
if (!content.includes(`\n${paraglideOutDir}`)) content = content.trimEnd() + `\n\n# Paraglide\n${paraglideOutDir}`;
|
|
2281
|
+
return content;
|
|
2282
|
+
});
|
|
2283
|
+
if (options$6.demo) {
|
|
2284
|
+
sv.file(`${kit.routesDirectory}/demo/+page.svelte`, (content) => {
|
|
2285
|
+
return addToDemoPage(content, "paraglide");
|
|
2286
|
+
});
|
|
2287
|
+
sv.file(`${kit.routesDirectory}/demo/paraglide/+page.svelte`, (content) => {
|
|
2288
|
+
const { script, template, generateCode } = parseSvelte(content, { typescript });
|
|
2289
|
+
addNamed(script.ast, {
|
|
2290
|
+
from: "$lib/paraglide/messages.js",
|
|
2291
|
+
imports: ["m"]
|
|
2292
|
+
});
|
|
2293
|
+
addNamed(script.ast, {
|
|
2294
|
+
from: "$app/navigation",
|
|
2295
|
+
imports: ["goto"]
|
|
2296
|
+
});
|
|
2297
|
+
addNamed(script.ast, {
|
|
2298
|
+
from: "$app/state",
|
|
2299
|
+
imports: ["page"]
|
|
2300
|
+
});
|
|
2301
|
+
addNamed(script.ast, {
|
|
2302
|
+
from: "$lib/paraglide/runtime",
|
|
2303
|
+
imports: ["setLocale"]
|
|
2304
|
+
});
|
|
2305
|
+
const scriptCode = new MagicString(script.generateCode());
|
|
2306
|
+
const templateCode = new MagicString(template.source);
|
|
2307
|
+
templateCode.append("\n\n<h1>{m.hello_world({ name: 'SvelteKit User' })}</h1>\n");
|
|
2308
|
+
const { validLanguageTags: validLanguageTags$1 } = parseLanguageTagInput(options$6.languageTags);
|
|
2309
|
+
const links = validLanguageTags$1.map((x$1) => `${templateCode.getIndentString()}<button onclick={() => setLocale('${x$1}')}>${x$1}</button>`).join("\n");
|
|
2310
|
+
templateCode.append(`<div>\n${links}\n</div>`);
|
|
2311
|
+
templateCode.append("<p>\nIf you use VSCode, install the <a href=\"https://marketplace.visualstudio.com/items?itemName=inlang.vs-code-extension\" target=\"_blank\">Sherlock i18n extension</a> for a better i18n experience.\n</p>");
|
|
2312
|
+
return generateCode({
|
|
2313
|
+
script: scriptCode.toString(),
|
|
2314
|
+
template: templateCode.toString()
|
|
2315
|
+
});
|
|
2316
|
+
});
|
|
2317
|
+
}
|
|
2318
|
+
const { validLanguageTags } = parseLanguageTagInput(options$6.languageTags);
|
|
2319
|
+
for (const languageTag of validLanguageTags) sv.file(`messages/${languageTag}.json`, (content) => {
|
|
2320
|
+
const { data, generateCode } = parseJson(content);
|
|
2321
|
+
data["$schema"] = "https://inlang.com/schema/inlang-message-format";
|
|
2322
|
+
data.hello_world = `Hello, {name} from ${languageTag}!`;
|
|
2323
|
+
return generateCode();
|
|
2324
|
+
});
|
|
2325
|
+
},
|
|
2326
|
+
nextSteps: ({ highlighter }) => {
|
|
2327
|
+
const steps = [`Edit your messages in ${highlighter.path("messages/en.json")}`];
|
|
2328
|
+
if (options$3.demo) steps.push(`Visit ${highlighter.route("/demo/paraglide")} route to view the demo`);
|
|
2329
|
+
return steps;
|
|
2330
|
+
}
|
|
2331
|
+
});
|
|
2332
|
+
const isValidLanguageTag = (languageTag) => RegExp("^((?<grandfathered>(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((?<language>([A-Za-z]{2,3}(-(?<extlang>[A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?))(-(?<script>[A-Za-z]{4}))?(-(?<region>[A-Za-z]{2}|[0-9]{3}))?(-(?<variant>[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*))$").test(languageTag);
|
|
2333
|
+
function parseLanguageTagInput(input) {
|
|
2334
|
+
const probablyLanguageTags = input.replace(/[,:\s]/g, " ").split(" ").filter(Boolean).map((tag) => tag.toLowerCase());
|
|
2335
|
+
const validLanguageTags = [];
|
|
2336
|
+
const invalidLanguageTags = [];
|
|
2337
|
+
for (const tag of probablyLanguageTags) if (isValidLanguageTag(tag)) validLanguageTags.push(tag);
|
|
2338
|
+
else invalidLanguageTags.push(tag);
|
|
2339
|
+
return {
|
|
2340
|
+
validLanguageTags,
|
|
2341
|
+
invalidLanguageTags
|
|
2342
|
+
};
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
//#endregion
|
|
2346
|
+
//#region ../addons/playwright/index.ts
|
|
2347
|
+
var playwright_default = defineAddon({
|
|
2348
|
+
id: "playwright",
|
|
2349
|
+
shortDescription: "browser testing",
|
|
2350
|
+
homepage: "https://playwright.dev",
|
|
2351
|
+
options: {},
|
|
2352
|
+
run: ({ sv, typescript }) => {
|
|
2353
|
+
const ext = typescript ? "ts" : "js";
|
|
2354
|
+
sv.devDependency("@playwright/test", "^1.55.1");
|
|
2355
|
+
sv.file("package.json", (content) => {
|
|
2356
|
+
const { data, generateCode } = parseJson(content);
|
|
2357
|
+
data.scripts ??= {};
|
|
2358
|
+
const scripts = data.scripts;
|
|
2359
|
+
const TEST_CMD = "playwright test";
|
|
2360
|
+
const RUN_TEST = "npm run test:e2e";
|
|
2361
|
+
scripts["test:e2e"] ??= TEST_CMD;
|
|
2362
|
+
scripts["test"] ??= RUN_TEST;
|
|
2363
|
+
if (!scripts["test"].includes(RUN_TEST)) scripts["test"] += ` && ${RUN_TEST}`;
|
|
2364
|
+
return generateCode();
|
|
2365
|
+
});
|
|
2366
|
+
sv.file(".gitignore", (content) => {
|
|
2367
|
+
if (!content) return content;
|
|
2368
|
+
if (content.includes("test-results")) return content;
|
|
2369
|
+
return "test-results\n" + content.trim();
|
|
2370
|
+
});
|
|
2371
|
+
sv.file(`e2e/demo.test.${ext}`, (content) => {
|
|
2372
|
+
if (content) return content;
|
|
2373
|
+
return dedent_default`
|
|
2374
|
+
import { expect, test } from '@playwright/test';
|
|
2375
|
+
|
|
2376
|
+
test('home page has expected h1', async ({ page }) => {
|
|
2377
|
+
await page.goto('/');
|
|
2378
|
+
await expect(page.locator('h1')).toBeVisible();
|
|
2379
|
+
});
|
|
2380
|
+
`;
|
|
2381
|
+
});
|
|
2382
|
+
sv.file(`playwright.config.${ext}`, (content) => {
|
|
2383
|
+
const { ast, generateCode } = parseScript(content);
|
|
2384
|
+
const defineConfig = parseExpression("defineConfig({})");
|
|
2385
|
+
const { value: defaultExport } = createDefault(ast, { fallback: defineConfig });
|
|
2386
|
+
const config = {
|
|
2387
|
+
webServer: {
|
|
2388
|
+
command: "npm run build && npm run preview",
|
|
2389
|
+
port: 4173
|
|
2390
|
+
},
|
|
2391
|
+
testDir: "e2e"
|
|
2392
|
+
};
|
|
2393
|
+
if (defaultExport.type === "CallExpression" && defaultExport.arguments[0]?.type === "ObjectExpression") {
|
|
2394
|
+
addNamed(ast, {
|
|
2395
|
+
imports: ["defineConfig"],
|
|
2396
|
+
from: "@playwright/test"
|
|
2397
|
+
});
|
|
2398
|
+
overrideProperties(defaultExport.arguments[0], config);
|
|
2399
|
+
} else if (defaultExport.type === "ObjectExpression") overrideProperties(defaultExport, config);
|
|
2400
|
+
else T.warn("Unexpected playwright config for playwright add-on. Could not update.");
|
|
2401
|
+
return generateCode();
|
|
2402
|
+
});
|
|
2403
|
+
}
|
|
2404
|
+
});
|
|
2405
|
+
|
|
2406
|
+
//#endregion
|
|
2407
|
+
//#region ../addons/prettier/index.ts
|
|
2408
|
+
var prettier_default = defineAddon({
|
|
2409
|
+
id: "prettier",
|
|
2410
|
+
shortDescription: "formatter",
|
|
2411
|
+
homepage: "https://prettier.io",
|
|
2412
|
+
options: {},
|
|
2413
|
+
run: ({ sv, dependencyVersion }) => {
|
|
2414
|
+
sv.devDependency("prettier", "^3.6.2");
|
|
2415
|
+
sv.devDependency("prettier-plugin-svelte", "^3.4.0");
|
|
2416
|
+
sv.file(".prettierignore", (content) => {
|
|
2417
|
+
if (content) return content;
|
|
2418
|
+
return dedent_default`
|
|
2419
|
+
# Package Managers
|
|
2420
|
+
package-lock.json
|
|
2421
|
+
pnpm-lock.yaml
|
|
2422
|
+
yarn.lock
|
|
2423
|
+
bun.lock
|
|
2424
|
+
bun.lockb
|
|
2425
|
+
|
|
2426
|
+
# Miscellaneous
|
|
2427
|
+
/static/
|
|
2428
|
+
`;
|
|
2429
|
+
});
|
|
2430
|
+
sv.file(".prettierrc", (content) => {
|
|
2431
|
+
let data, generateCode;
|
|
2432
|
+
try {
|
|
2433
|
+
({data, generateCode} = parseJson(content));
|
|
2434
|
+
} catch {
|
|
2435
|
+
T.warn(`A ${import_picocolors$2.default.yellow(".prettierrc")} config already exists and cannot be parsed as JSON. Skipping initialization.`);
|
|
2436
|
+
return content;
|
|
2437
|
+
}
|
|
2438
|
+
if (Object.keys(data).length === 0) {
|
|
2439
|
+
data.useTabs = true;
|
|
2440
|
+
data.singleQuote = true;
|
|
2441
|
+
data.trailingComma = "none";
|
|
2442
|
+
data.printWidth = 100;
|
|
2443
|
+
}
|
|
2444
|
+
data.plugins ??= [];
|
|
2445
|
+
data.overrides ??= [];
|
|
2446
|
+
if (!data.plugins.includes("prettier-plugin-svelte")) data.plugins.unshift("prettier-plugin-svelte");
|
|
2447
|
+
const overrides = data.overrides;
|
|
2448
|
+
if (!overrides.find((o) => o?.options?.parser === "svelte")) overrides.push({
|
|
2449
|
+
files: "*.svelte",
|
|
2450
|
+
options: { parser: "svelte" }
|
|
2451
|
+
});
|
|
2452
|
+
return generateCode();
|
|
2453
|
+
});
|
|
2454
|
+
const eslintVersion = dependencyVersion("eslint");
|
|
2455
|
+
const eslintInstalled = hasEslint(eslintVersion);
|
|
2456
|
+
sv.file("package.json", (content) => {
|
|
2457
|
+
const { data, generateCode } = parseJson(content);
|
|
2458
|
+
data.scripts ??= {};
|
|
2459
|
+
const scripts = data.scripts;
|
|
2460
|
+
const CHECK_CMD = "prettier --check .";
|
|
2461
|
+
scripts["format"] ??= "prettier --write .";
|
|
2462
|
+
if (eslintInstalled) {
|
|
2463
|
+
scripts["lint"] ??= `${CHECK_CMD} && eslint .`;
|
|
2464
|
+
if (!scripts["lint"].includes(CHECK_CMD)) scripts["lint"] += ` && ${CHECK_CMD}`;
|
|
2465
|
+
} else scripts["lint"] ??= CHECK_CMD;
|
|
2466
|
+
return generateCode();
|
|
2467
|
+
});
|
|
2468
|
+
if (eslintVersion?.startsWith(SUPPORTED_ESLINT_VERSION) === false) T.warn(`An older major version of ${import_picocolors$2.default.yellow("eslint")} was detected. Skipping ${import_picocolors$2.default.yellow("eslint-config-prettier")} installation.`);
|
|
2469
|
+
if (eslintInstalled) {
|
|
2470
|
+
sv.devDependency("eslint-config-prettier", "^10.1.8");
|
|
2471
|
+
sv.file("eslint.config.js", addEslintConfigPrettier);
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
});
|
|
2475
|
+
const SUPPORTED_ESLINT_VERSION = "9";
|
|
2476
|
+
function hasEslint(version) {
|
|
2477
|
+
return !!version && version.startsWith(SUPPORTED_ESLINT_VERSION);
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
//#endregion
|
|
2481
|
+
//#region ../addons/storybook/index.ts
|
|
2482
|
+
var storybook_default = defineAddon({
|
|
2483
|
+
id: "storybook",
|
|
2484
|
+
shortDescription: "frontend workshop",
|
|
2485
|
+
homepage: "https://storybook.js.org",
|
|
2486
|
+
options: {},
|
|
2487
|
+
setup: ({ runsAfter }) => {
|
|
2488
|
+
runsAfter("vitest");
|
|
2489
|
+
runsAfter("eslint");
|
|
2490
|
+
},
|
|
2491
|
+
run: async ({ sv }) => {
|
|
2492
|
+
const args = [
|
|
2493
|
+
"create-storybook@latest",
|
|
2494
|
+
"--skip-install",
|
|
2495
|
+
"--no-dev"
|
|
2496
|
+
];
|
|
2497
|
+
if (process.env.NODE_ENV?.toLowerCase() === "test") args.push("--yes");
|
|
2498
|
+
await sv.execute(args, "inherit");
|
|
2499
|
+
sv.devDependency(`@types/node`, getNodeTypesVersion());
|
|
2500
|
+
}
|
|
2501
|
+
});
|
|
2502
|
+
|
|
2503
|
+
//#endregion
|
|
2504
|
+
//#region ../addons/sveltekit-adapter/index.ts
|
|
2505
|
+
const adapters = [
|
|
2506
|
+
{
|
|
2507
|
+
id: "auto",
|
|
2508
|
+
package: "@sveltejs/adapter-auto",
|
|
2509
|
+
version: "^6.1.0"
|
|
2510
|
+
},
|
|
2511
|
+
{
|
|
2512
|
+
id: "node",
|
|
2513
|
+
package: "@sveltejs/adapter-node",
|
|
2514
|
+
version: "^5.3.2"
|
|
2515
|
+
},
|
|
2516
|
+
{
|
|
2517
|
+
id: "static",
|
|
2518
|
+
package: "@sveltejs/adapter-static",
|
|
2519
|
+
version: "^3.0.9"
|
|
2520
|
+
},
|
|
2521
|
+
{
|
|
2522
|
+
id: "vercel",
|
|
2523
|
+
package: "@sveltejs/adapter-vercel",
|
|
2524
|
+
version: "^5.10.2"
|
|
2525
|
+
},
|
|
2526
|
+
{
|
|
2527
|
+
id: "cloudflare",
|
|
2528
|
+
package: "@sveltejs/adapter-cloudflare",
|
|
2529
|
+
version: "^7.2.3"
|
|
2530
|
+
},
|
|
2531
|
+
{
|
|
2532
|
+
id: "netlify",
|
|
2533
|
+
package: "@sveltejs/adapter-netlify",
|
|
2534
|
+
version: "^5.2.3"
|
|
2535
|
+
}
|
|
2536
|
+
];
|
|
2537
|
+
const options$2 = defineAddonOptions().add("adapter", {
|
|
2538
|
+
type: "select",
|
|
2539
|
+
question: "Which SvelteKit adapter would you like to use?",
|
|
2540
|
+
default: "auto",
|
|
2541
|
+
options: adapters.map((p) => ({
|
|
2542
|
+
value: p.id,
|
|
2543
|
+
label: p.id,
|
|
2544
|
+
hint: p.package
|
|
2545
|
+
}))
|
|
2546
|
+
}).build();
|
|
2547
|
+
var sveltekit_adapter_default = defineAddon({
|
|
2548
|
+
id: "sveltekit-adapter",
|
|
2549
|
+
alias: "adapter",
|
|
2550
|
+
shortDescription: "deployment",
|
|
2551
|
+
homepage: "https://svelte.dev/docs/kit/adapters",
|
|
2552
|
+
options: options$2,
|
|
2553
|
+
setup: ({ kit, unsupported }) => {
|
|
2554
|
+
if (!kit) unsupported("Requires SvelteKit");
|
|
2555
|
+
},
|
|
2556
|
+
run: ({ sv, options: options$6 }) => {
|
|
2557
|
+
const adapter = adapters.find((a) => a.id === options$6.adapter);
|
|
2558
|
+
sv.file("package.json", (content) => {
|
|
2559
|
+
const { data, generateCode } = parseJson(content);
|
|
2560
|
+
const devDeps = data["devDependencies"];
|
|
2561
|
+
for (const pkg of Object.keys(devDeps)) if (pkg.startsWith("@sveltejs/adapter-")) delete devDeps[pkg];
|
|
2562
|
+
return generateCode();
|
|
2563
|
+
});
|
|
2564
|
+
sv.devDependency(adapter.package, adapter.version);
|
|
2565
|
+
sv.file("svelte.config.js", (content) => {
|
|
2566
|
+
const { ast, generateCode } = parseScript(content);
|
|
2567
|
+
const adapterImportDecl = ast.body.filter((n) => n.type === "ImportDeclaration").find((importDecl) => typeof importDecl.source.value === "string" && importDecl.source.value.startsWith("@sveltejs/adapter-") && importDecl.importKind === "value");
|
|
2568
|
+
let adapterName = "adapter";
|
|
2569
|
+
if (adapterImportDecl) {
|
|
2570
|
+
adapterImportDecl.source.value = adapter.package;
|
|
2571
|
+
adapterImportDecl.source.raw = void 0;
|
|
2572
|
+
adapterName = adapterImportDecl.specifiers?.find((s) => s.type === "ImportDefaultSpecifier")?.local?.name;
|
|
2573
|
+
} else addDefault(ast, {
|
|
2574
|
+
from: adapter.package,
|
|
2575
|
+
as: adapterName
|
|
2576
|
+
});
|
|
2577
|
+
const { value: config } = createDefault(ast, { fallback: create({}) });
|
|
2578
|
+
overrideProperties(config, { kit: { adapter: createCall({
|
|
2579
|
+
name: adapterName,
|
|
2580
|
+
args: [],
|
|
2581
|
+
useIdentifiers: true
|
|
2582
|
+
}) } });
|
|
2583
|
+
if (adapter.package !== "@sveltejs/adapter-auto") {
|
|
2584
|
+
const fallback = create({});
|
|
2585
|
+
const cfgKitValue = property(config, {
|
|
2586
|
+
name: "kit",
|
|
2587
|
+
fallback
|
|
2588
|
+
});
|
|
2589
|
+
const cfgAdapter = propertyNode(cfgKitValue, {
|
|
2590
|
+
name: "adapter",
|
|
2591
|
+
fallback
|
|
2592
|
+
});
|
|
2593
|
+
cfgAdapter.leadingComments = [];
|
|
2594
|
+
}
|
|
2595
|
+
return generateCode();
|
|
2596
|
+
});
|
|
2597
|
+
}
|
|
2598
|
+
});
|
|
2599
|
+
|
|
2600
|
+
//#endregion
|
|
2601
|
+
//#region ../addons/tailwindcss/index.ts
|
|
2602
|
+
const plugins = [{
|
|
2603
|
+
id: "typography",
|
|
2604
|
+
package: "@tailwindcss/typography",
|
|
2605
|
+
version: "^0.5.18"
|
|
2606
|
+
}, {
|
|
2607
|
+
id: "forms",
|
|
2608
|
+
package: "@tailwindcss/forms",
|
|
2609
|
+
version: "^0.5.10"
|
|
2610
|
+
}];
|
|
2611
|
+
const options$1 = defineAddonOptions().add("plugins", {
|
|
2612
|
+
type: "multiselect",
|
|
2613
|
+
question: "Which plugins would you like to add?",
|
|
2614
|
+
options: plugins.map((p) => ({
|
|
2615
|
+
value: p.id,
|
|
2616
|
+
label: p.id,
|
|
2617
|
+
hint: p.package
|
|
2618
|
+
})),
|
|
2619
|
+
default: [],
|
|
2620
|
+
required: false
|
|
2621
|
+
}).build();
|
|
2622
|
+
var tailwindcss_default = defineAddon({
|
|
2623
|
+
id: "tailwindcss",
|
|
2624
|
+
alias: "tailwind",
|
|
2625
|
+
shortDescription: "css framework",
|
|
2626
|
+
homepage: "https://tailwindcss.com",
|
|
2627
|
+
options: options$1,
|
|
2628
|
+
run: ({ sv, options: options$6, viteConfigFile, typescript, kit, dependencyVersion }) => {
|
|
2629
|
+
const prettierInstalled = Boolean(dependencyVersion("prettier"));
|
|
2630
|
+
sv.devDependency("tailwindcss", "^4.1.13");
|
|
2631
|
+
sv.devDependency("@tailwindcss/vite", "^4.1.13");
|
|
2632
|
+
if (prettierInstalled) sv.devDependency("prettier-plugin-tailwindcss", "^0.6.14");
|
|
2633
|
+
for (const plugin of plugins) {
|
|
2634
|
+
if (!options$6.plugins.includes(plugin.id)) continue;
|
|
2635
|
+
sv.devDependency(plugin.package, plugin.version);
|
|
2636
|
+
}
|
|
2637
|
+
sv.file(viteConfigFile, (content) => {
|
|
2638
|
+
const { ast, generateCode } = parseScript(content);
|
|
2639
|
+
const vitePluginName = "tailwindcss";
|
|
2640
|
+
addDefault(ast, {
|
|
2641
|
+
as: vitePluginName,
|
|
2642
|
+
from: "@tailwindcss/vite"
|
|
2643
|
+
});
|
|
2644
|
+
addPlugin(ast, {
|
|
2645
|
+
code: `${vitePluginName}()`,
|
|
2646
|
+
mode: "prepend"
|
|
2647
|
+
});
|
|
2648
|
+
return generateCode();
|
|
2649
|
+
});
|
|
2650
|
+
sv.file("src/app.css", (content) => {
|
|
2651
|
+
let atRules = parseCss(content).ast.nodes.filter((node) => node.type === "atrule");
|
|
2652
|
+
const findAtRule = (name, params) => atRules.find((rule) => rule.name === name && rule.params.replace(/['"]/g, "") === params);
|
|
2653
|
+
let code = content;
|
|
2654
|
+
if (!findAtRule("import", "tailwindcss")) {
|
|
2655
|
+
code = "@import 'tailwindcss';\n" + code;
|
|
2656
|
+
atRules = parseCss(code).ast.nodes.filter((node) => node.type === "atrule");
|
|
2657
|
+
}
|
|
2658
|
+
const pluginPos = atRules.findLast((rule) => ["plugin", "import"].includes(rule.name)).source.end.offset;
|
|
2659
|
+
for (const plugin of plugins) {
|
|
2660
|
+
if (!options$6.plugins.includes(plugin.id)) continue;
|
|
2661
|
+
if (!findAtRule("plugin", plugin.package)) {
|
|
2662
|
+
const pluginImport = `\n@plugin '${plugin.package}';`;
|
|
2663
|
+
code = code.substring(0, pluginPos) + pluginImport + code.substring(pluginPos);
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
return code;
|
|
2667
|
+
});
|
|
2668
|
+
if (!kit) sv.file("src/App.svelte", (content) => {
|
|
2669
|
+
const { script, generateCode } = parseSvelte(content, { typescript });
|
|
2670
|
+
addEmpty(script.ast, { from: "./app.css" });
|
|
2671
|
+
return generateCode({ script: script.generateCode() });
|
|
2672
|
+
});
|
|
2673
|
+
else sv.file(`${kit?.routesDirectory}/+layout.svelte`, (content) => {
|
|
2674
|
+
const { script, template, generateCode } = parseSvelte(content, { typescript });
|
|
2675
|
+
addEmpty(script.ast, { from: "../app.css" });
|
|
2676
|
+
if (content.length === 0) {
|
|
2677
|
+
const svelteVersion = dependencyVersion("svelte");
|
|
2678
|
+
if (!svelteVersion) throw new Error("Failed to determine svelte version");
|
|
2679
|
+
addSlot(script.ast, {
|
|
2680
|
+
htmlAst: template.ast,
|
|
2681
|
+
svelteVersion
|
|
2682
|
+
});
|
|
2683
|
+
}
|
|
2684
|
+
return generateCode({
|
|
2685
|
+
script: script.generateCode(),
|
|
2686
|
+
template: content.length === 0 ? template.generateCode() : void 0
|
|
2687
|
+
});
|
|
2688
|
+
});
|
|
2689
|
+
if (dependencyVersion("prettier")) sv.file(".prettierrc", (content) => {
|
|
2690
|
+
const { data, generateCode } = parseJson(content);
|
|
2691
|
+
const PLUGIN_NAME = "prettier-plugin-tailwindcss";
|
|
2692
|
+
data.plugins ??= [];
|
|
2693
|
+
const plugins$1 = data.plugins;
|
|
2694
|
+
if (!plugins$1.includes(PLUGIN_NAME)) plugins$1.push(PLUGIN_NAME);
|
|
2695
|
+
data.tailwindStylesheet ??= "./src/app.css";
|
|
2696
|
+
return generateCode();
|
|
2697
|
+
});
|
|
2698
|
+
}
|
|
2699
|
+
});
|
|
2700
|
+
|
|
2701
|
+
//#endregion
|
|
2702
|
+
//#region ../addons/vitest-addon/index.ts
|
|
2703
|
+
const options = defineAddonOptions().add("usages", {
|
|
2704
|
+
question: "What do you want to use vitest for?",
|
|
2705
|
+
type: "multiselect",
|
|
2706
|
+
default: ["unit", "component"],
|
|
2707
|
+
options: [{
|
|
2708
|
+
value: "unit",
|
|
2709
|
+
label: "unit testing"
|
|
2710
|
+
}, {
|
|
2711
|
+
value: "component",
|
|
2712
|
+
label: "component testing"
|
|
2713
|
+
}],
|
|
2714
|
+
required: true
|
|
2715
|
+
}).build();
|
|
2716
|
+
var vitest_addon_default = defineAddon({
|
|
2717
|
+
id: "vitest",
|
|
2718
|
+
shortDescription: "unit testing",
|
|
2719
|
+
homepage: "https://vitest.dev",
|
|
2720
|
+
options,
|
|
2721
|
+
run: ({ sv, viteConfigFile, typescript, kit, options: options$6 }) => {
|
|
2722
|
+
const ext = typescript ? "ts" : "js";
|
|
2723
|
+
const unitTesting = options$6.usages.includes("unit");
|
|
2724
|
+
const componentTesting = options$6.usages.includes("component");
|
|
2725
|
+
sv.devDependency("vitest", "^3.2.4");
|
|
2726
|
+
if (componentTesting) {
|
|
2727
|
+
sv.devDependency("@vitest/browser", "^3.2.4");
|
|
2728
|
+
sv.devDependency("vitest-browser-svelte", "^1.1.0");
|
|
2729
|
+
sv.devDependency("playwright", "^1.55.1");
|
|
2730
|
+
}
|
|
2731
|
+
sv.file("package.json", (content) => {
|
|
2732
|
+
const { data, generateCode } = parseJson(content);
|
|
2733
|
+
data.scripts ??= {};
|
|
2734
|
+
const scripts = data.scripts;
|
|
2735
|
+
const TEST_CMD = "vitest";
|
|
2736
|
+
const RUN_TEST = "npm run test:unit -- --run";
|
|
2737
|
+
scripts["test:unit"] ??= TEST_CMD;
|
|
2738
|
+
scripts["test"] ??= RUN_TEST;
|
|
2739
|
+
if (!scripts["test"].includes(RUN_TEST)) scripts["test"] += ` && ${RUN_TEST}`;
|
|
2740
|
+
return generateCode();
|
|
2741
|
+
});
|
|
2742
|
+
if (unitTesting) sv.file(`src/demo.spec.${ext}`, (content) => {
|
|
2743
|
+
if (content) return content;
|
|
2744
|
+
return dedent_default`
|
|
2745
|
+
import { describe, it, expect } from 'vitest';
|
|
2746
|
+
|
|
2747
|
+
describe('sum test', () => {
|
|
2748
|
+
it('adds 1 + 2 to equal 3', () => {
|
|
2749
|
+
expect(1 + 2).toBe(3);
|
|
2750
|
+
});
|
|
2751
|
+
});
|
|
2752
|
+
`;
|
|
2753
|
+
});
|
|
2754
|
+
if (componentTesting) {
|
|
2755
|
+
const fileName = kit ? `${kit.routesDirectory}/page.svelte.spec.${ext}` : `src/App.svelte.test.${ext}`;
|
|
2756
|
+
sv.file(fileName, (content) => {
|
|
2757
|
+
if (content) return content;
|
|
2758
|
+
return dedent_default`
|
|
2759
|
+
import { page } from '@vitest/browser/context';
|
|
2760
|
+
import { describe, expect, it } from 'vitest';
|
|
2761
|
+
import { render } from 'vitest-browser-svelte';
|
|
2762
|
+
${kit ? "import Page from './+page.svelte';" : "import App from './App.svelte';"}
|
|
2763
|
+
|
|
2764
|
+
describe('${kit ? "/+page.svelte" : "App.svelte"}', () => {
|
|
2765
|
+
it('should render h1', async () => {
|
|
2766
|
+
render(${kit ? "Page" : "App"});
|
|
2767
|
+
|
|
2768
|
+
const heading = page.getByRole('heading', { level: 1 });
|
|
2769
|
+
await expect.element(heading).toBeInTheDocument();
|
|
2770
|
+
});
|
|
2771
|
+
});
|
|
2772
|
+
`;
|
|
2773
|
+
});
|
|
2774
|
+
sv.file(`vitest-setup-client.${ext}`, (content) => {
|
|
2775
|
+
if (content) return content;
|
|
2776
|
+
return dedent_default`
|
|
2777
|
+
/// <reference types="@vitest/browser/matchers" />
|
|
2778
|
+
/// <reference types="@vitest/browser/providers/playwright" />
|
|
2779
|
+
`;
|
|
2780
|
+
});
|
|
2781
|
+
}
|
|
2782
|
+
sv.file(viteConfigFile, (content) => {
|
|
2783
|
+
const { ast, generateCode } = parseScript(content);
|
|
2784
|
+
const clientObjectExpression = create({
|
|
2785
|
+
extends: `./${viteConfigFile}`,
|
|
2786
|
+
test: {
|
|
2787
|
+
name: "client",
|
|
2788
|
+
environment: "browser",
|
|
2789
|
+
browser: {
|
|
2790
|
+
enabled: true,
|
|
2791
|
+
provider: "playwright",
|
|
2792
|
+
instances: [{ browser: "chromium" }]
|
|
2793
|
+
},
|
|
2794
|
+
include: ["src/**/*.svelte.{test,spec}.{js,ts}"],
|
|
2795
|
+
exclude: ["src/lib/server/**"],
|
|
2796
|
+
setupFiles: [`./vitest-setup-client.${ext}`]
|
|
2797
|
+
}
|
|
2798
|
+
});
|
|
2799
|
+
const serverObjectExpression = create({
|
|
2800
|
+
extends: `./${viteConfigFile}`,
|
|
2801
|
+
test: {
|
|
2802
|
+
name: "server",
|
|
2803
|
+
environment: "node",
|
|
2804
|
+
include: ["src/**/*.{test,spec}.{js,ts}"],
|
|
2805
|
+
exclude: ["src/**/*.svelte.{test,spec}.{js,ts}"]
|
|
2806
|
+
}
|
|
2807
|
+
});
|
|
2808
|
+
const viteConfig = getConfig(ast);
|
|
2809
|
+
const testObject = property(viteConfig, {
|
|
2810
|
+
name: "test",
|
|
2811
|
+
fallback: create({ expect: { requireAssertions: true } })
|
|
2812
|
+
});
|
|
2813
|
+
const workspaceArray = property(testObject, {
|
|
2814
|
+
name: "projects",
|
|
2815
|
+
fallback: create$1()
|
|
2816
|
+
});
|
|
2817
|
+
if (componentTesting) append(workspaceArray, clientObjectExpression);
|
|
2818
|
+
if (unitTesting) append(workspaceArray, serverObjectExpression);
|
|
2819
|
+
const importName = "defineConfig";
|
|
2820
|
+
const { statement, alias } = find(ast, {
|
|
2821
|
+
name: importName,
|
|
2822
|
+
from: "vite"
|
|
2823
|
+
});
|
|
2824
|
+
if (statement) {
|
|
2825
|
+
addNamed(ast, {
|
|
2826
|
+
imports: { defineConfig: alias },
|
|
2827
|
+
from: "vitest/config"
|
|
2828
|
+
});
|
|
2829
|
+
remove(ast, {
|
|
2830
|
+
name: importName,
|
|
2831
|
+
from: "vite",
|
|
2832
|
+
statement
|
|
2833
|
+
});
|
|
2834
|
+
}
|
|
2835
|
+
return generateCode();
|
|
2836
|
+
});
|
|
2837
|
+
}
|
|
2838
|
+
});
|
|
2839
|
+
|
|
2840
|
+
//#endregion
|
|
2841
|
+
//#region ../addons/_config/official.ts
|
|
2842
|
+
const officialAddons = {
|
|
2843
|
+
prettier: prettier_default,
|
|
2844
|
+
eslint: eslint_default,
|
|
2845
|
+
vitest: vitest_addon_default,
|
|
2846
|
+
playwright: playwright_default,
|
|
2847
|
+
tailwindcss: tailwindcss_default,
|
|
2848
|
+
sveltekitAdapter: sveltekit_adapter_default,
|
|
2849
|
+
devtoolsJson: devtools_json_default,
|
|
2850
|
+
drizzle: drizzle_default,
|
|
2851
|
+
lucia: lucia_default,
|
|
2852
|
+
mdsvex: mdsvex_default,
|
|
2853
|
+
paraglide: paraglide_default,
|
|
2854
|
+
storybook: storybook_default
|
|
2855
|
+
};
|
|
2856
|
+
function getAddonDetails(id) {
|
|
2857
|
+
const details = Object.values(officialAddons).find((a) => a.id === id);
|
|
2858
|
+
if (!details) throw new Error(`Invalid add-on: ${id}`);
|
|
2859
|
+
return details;
|
|
2860
|
+
}
|
|
2861
|
+
|
|
2862
|
+
//#endregion
|
|
2863
|
+
//#region ../addons/_config/community.ts
|
|
2864
|
+
/** EVALUATED AT BUILD TIME */
|
|
2865
|
+
const communityAddonIds = ["unocss", "unplugin-icons"];
|
|
2866
|
+
async function getCommunityAddon(name) {
|
|
2867
|
+
const { default: details } = await import(`../../../community-addons/${name}.ts`);
|
|
2868
|
+
return details;
|
|
2869
|
+
}
|
|
2870
|
+
|
|
2871
|
+
//#endregion
|
|
2872
|
+
//#region commands/add/utils.ts
|
|
2873
|
+
var import_picocolors$1 = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
2874
|
+
function getPackageJson(cwd) {
|
|
2875
|
+
const packageText = readFile(cwd, commonFilePaths.packageJson);
|
|
2876
|
+
if (!packageText) {
|
|
2877
|
+
const pkgPath = path.join(cwd, commonFilePaths.packageJson);
|
|
2878
|
+
throw new Error(`Invalid workspace: missing '${pkgPath}'`);
|
|
2879
|
+
}
|
|
2880
|
+
const { data, generateCode } = parseJson(packageText);
|
|
2881
|
+
return {
|
|
2882
|
+
source: packageText,
|
|
2883
|
+
data,
|
|
2884
|
+
generateCode
|
|
2885
|
+
};
|
|
2886
|
+
}
|
|
2887
|
+
async function formatFiles(options$6) {
|
|
2888
|
+
const args = [
|
|
2889
|
+
"prettier",
|
|
2890
|
+
"--write",
|
|
2891
|
+
"--ignore-unknown",
|
|
2892
|
+
...options$6.paths
|
|
2893
|
+
];
|
|
2894
|
+
const cmd = resolveCommand(options$6.packageManager, "execute-local", args);
|
|
2895
|
+
await be(cmd.command, cmd.args, {
|
|
2896
|
+
nodeOptions: {
|
|
2897
|
+
cwd: options$6.cwd,
|
|
2898
|
+
stdio: "pipe"
|
|
2899
|
+
},
|
|
2900
|
+
throwOnError: true
|
|
2901
|
+
});
|
|
2902
|
+
}
|
|
2903
|
+
function readFile(cwd, filePath) {
|
|
2904
|
+
const fullFilePath = path.resolve(cwd, filePath);
|
|
2905
|
+
if (!fileExists(cwd, filePath)) return "";
|
|
2906
|
+
return fs.readFileSync(fullFilePath, "utf8");
|
|
2907
|
+
}
|
|
2908
|
+
function installPackages(dependencies, workspace) {
|
|
2909
|
+
const { data, generateCode } = getPackageJson(workspace.cwd);
|
|
2910
|
+
for (const dependency of dependencies) if (dependency.dev) {
|
|
2911
|
+
data.devDependencies ??= {};
|
|
2912
|
+
data.devDependencies[dependency.pkg] = dependency.version;
|
|
2913
|
+
} else {
|
|
2914
|
+
data.dependencies ??= {};
|
|
2915
|
+
data.dependencies[dependency.pkg] = dependency.version;
|
|
2916
|
+
}
|
|
2917
|
+
if (data.dependencies) data.dependencies = alphabetizeProperties(data.dependencies);
|
|
2918
|
+
if (data.devDependencies) data.devDependencies = alphabetizeProperties(data.devDependencies);
|
|
2919
|
+
writeFile(workspace, commonFilePaths.packageJson, generateCode());
|
|
2920
|
+
return commonFilePaths.packageJson;
|
|
2921
|
+
}
|
|
2922
|
+
function alphabetizeProperties(obj) {
|
|
2923
|
+
const orderedObj = {};
|
|
2924
|
+
const sortedEntries = Object.entries(obj).sort(([a], [b]) => a.localeCompare(b));
|
|
2925
|
+
for (const [key, value] of sortedEntries) orderedObj[key] = value;
|
|
2926
|
+
return orderedObj;
|
|
2927
|
+
}
|
|
2928
|
+
function writeFile(workspace, filePath, content) {
|
|
2929
|
+
const fullFilePath = path.resolve(workspace.cwd, filePath);
|
|
2930
|
+
const fullDirectoryPath = path.dirname(fullFilePath);
|
|
2931
|
+
if (content && !content.endsWith("\n")) content += "\n";
|
|
2932
|
+
if (!fs.existsSync(fullDirectoryPath)) fs.mkdirSync(fullDirectoryPath, { recursive: true });
|
|
2933
|
+
fs.writeFileSync(fullFilePath, content, "utf8");
|
|
2934
|
+
}
|
|
2935
|
+
function fileExists(cwd, filePath) {
|
|
2936
|
+
const fullFilePath = path.resolve(cwd, filePath);
|
|
2937
|
+
return fs.existsSync(fullFilePath);
|
|
2938
|
+
}
|
|
2939
|
+
const commonFilePaths = {
|
|
2940
|
+
packageJson: "package.json",
|
|
2941
|
+
svelteConfig: "svelte.config.js",
|
|
2942
|
+
jsconfig: "jsconfig.json",
|
|
2943
|
+
tsconfig: "tsconfig.json",
|
|
2944
|
+
viteConfig: "vite.config.js",
|
|
2945
|
+
viteConfigTS: "vite.config.ts"
|
|
2946
|
+
};
|
|
2947
|
+
function getHighlighter() {
|
|
2948
|
+
return {
|
|
2949
|
+
command: (str) => import_picocolors$1.default.bold(import_picocolors$1.default.cyanBright(str)),
|
|
2950
|
+
env: (str) => import_picocolors$1.default.yellow(str),
|
|
2951
|
+
path: (str) => import_picocolors$1.default.green(str),
|
|
2952
|
+
route: (str) => import_picocolors$1.default.bold(str),
|
|
2953
|
+
website: (str) => import_picocolors$1.default.whiteBright(str)
|
|
2954
|
+
};
|
|
2955
|
+
}
|
|
2956
|
+
|
|
2957
|
+
//#endregion
|
|
2958
|
+
//#region commands/add/workspace.ts
|
|
2959
|
+
async function createWorkspace({ cwd, options: options$6 = {}, packageManager }) {
|
|
2960
|
+
const resolvedCwd = path.resolve(cwd);
|
|
2961
|
+
const usesTypescript = !any([commonFilePaths.jsconfig, commonFilePaths.tsconfig], { cwd })?.endsWith(commonFilePaths.jsconfig);
|
|
2962
|
+
const viteConfigPath = path.join(resolvedCwd, commonFilePaths.viteConfigTS);
|
|
2963
|
+
const viteConfigFile = fs.existsSync(viteConfigPath) ? commonFilePaths.viteConfigTS : commonFilePaths.viteConfig;
|
|
2964
|
+
let dependencies = {};
|
|
2965
|
+
let directory = resolvedCwd;
|
|
2966
|
+
const root = findRoot(resolvedCwd);
|
|
2967
|
+
while (directory && directory !== root) {
|
|
2968
|
+
if (fs.existsSync(path.join(directory, commonFilePaths.packageJson))) {
|
|
2969
|
+
const { data: packageJson } = getPackageJson(directory);
|
|
2970
|
+
dependencies = {
|
|
2971
|
+
...packageJson.devDependencies,
|
|
2972
|
+
...packageJson.dependencies,
|
|
2973
|
+
...dependencies
|
|
2974
|
+
};
|
|
2975
|
+
}
|
|
2976
|
+
directory = path.dirname(directory);
|
|
2977
|
+
}
|
|
2978
|
+
for (const [key, value] of Object.entries(dependencies)) dependencies[key] = value.replaceAll(/[^\d|.]/g, "");
|
|
2979
|
+
return {
|
|
2980
|
+
cwd: resolvedCwd,
|
|
2981
|
+
options: options$6,
|
|
2982
|
+
packageManager: packageManager ?? (await detect({ cwd }))?.name ?? getUserAgent() ?? "npm",
|
|
2983
|
+
typescript: usesTypescript,
|
|
2984
|
+
viteConfigFile,
|
|
2985
|
+
kit: dependencies["@sveltejs/kit"] ? parseKitOptions(resolvedCwd) : void 0,
|
|
2986
|
+
dependencyVersion: (pkg) => dependencies[pkg]
|
|
2987
|
+
};
|
|
2988
|
+
}
|
|
2989
|
+
function findRoot(cwd) {
|
|
2990
|
+
const { root } = path.parse(cwd);
|
|
2991
|
+
let directory = cwd;
|
|
2992
|
+
while (directory && directory !== root) {
|
|
2993
|
+
if (fs.existsSync(path.join(directory, commonFilePaths.packageJson))) {
|
|
2994
|
+
if (fs.existsSync(path.join(directory, "pnpm-workspace.yaml"))) return directory;
|
|
2995
|
+
const { data } = getPackageJson(directory);
|
|
2996
|
+
if (data.workspaces) return directory;
|
|
2997
|
+
}
|
|
2998
|
+
directory = path.dirname(directory);
|
|
2999
|
+
}
|
|
3000
|
+
return root;
|
|
3001
|
+
}
|
|
3002
|
+
function parseKitOptions(cwd) {
|
|
3003
|
+
const configSource = readFile(cwd, commonFilePaths.svelteConfig);
|
|
3004
|
+
const { ast } = parseScript(configSource);
|
|
3005
|
+
const defaultExport = ast.body.find((s) => s.type === "ExportDefaultDeclaration");
|
|
3006
|
+
if (!defaultExport) throw Error("Missing default export in `svelte.config.js`");
|
|
3007
|
+
let objectExpression;
|
|
3008
|
+
if (defaultExport.declaration.type === "Identifier") {
|
|
3009
|
+
const identifier = defaultExport.declaration;
|
|
3010
|
+
for (const declaration$1 of ast.body) {
|
|
3011
|
+
if (declaration$1.type !== "VariableDeclaration") continue;
|
|
3012
|
+
const declarator = declaration$1.declarations.find((d) => d.type === "VariableDeclarator" && d.id.type === "Identifier" && d.id.name === identifier.name);
|
|
3013
|
+
if (declarator?.init?.type !== "ObjectExpression") continue;
|
|
3014
|
+
objectExpression = declarator.init;
|
|
3015
|
+
}
|
|
3016
|
+
if (!objectExpression) throw Error("Unable to find svelte config object expression from `svelte.config.js`");
|
|
3017
|
+
} else if (defaultExport.declaration.type === "ObjectExpression") objectExpression = defaultExport.declaration;
|
|
3018
|
+
if (!objectExpression) throw new Error("Unexpected svelte config shape from `svelte.config.js`");
|
|
3019
|
+
const kit = property(objectExpression, {
|
|
3020
|
+
name: "kit",
|
|
3021
|
+
fallback: create({})
|
|
3022
|
+
});
|
|
3023
|
+
const files = property(kit, {
|
|
3024
|
+
name: "files",
|
|
3025
|
+
fallback: create({})
|
|
3026
|
+
});
|
|
3027
|
+
const routes = property(files, {
|
|
3028
|
+
name: "routes",
|
|
3029
|
+
fallback: createLiteral("")
|
|
3030
|
+
});
|
|
3031
|
+
const lib = property(files, {
|
|
3032
|
+
name: "lib",
|
|
3033
|
+
fallback: createLiteral("")
|
|
3034
|
+
});
|
|
3035
|
+
const routesDirectory = routes.value || "src/routes";
|
|
3036
|
+
const libDirectory = lib.value || "src/lib";
|
|
3037
|
+
return {
|
|
3038
|
+
routesDirectory,
|
|
3039
|
+
libDirectory
|
|
3040
|
+
};
|
|
3041
|
+
}
|
|
3042
|
+
|
|
3043
|
+
//#endregion
|
|
3044
|
+
//#region utils/env.ts
|
|
3045
|
+
const TESTING = process.env.NODE_ENV?.toLowerCase() === "test";
|
|
3046
|
+
|
|
3047
|
+
//#endregion
|
|
3048
|
+
//#region lib/install.ts
|
|
3049
|
+
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
3050
|
+
async function installAddon({ addons, cwd, options: options$6, packageManager = "npm" }) {
|
|
3051
|
+
const workspace = await createWorkspace({
|
|
3052
|
+
cwd,
|
|
3053
|
+
packageManager
|
|
3054
|
+
});
|
|
3055
|
+
const addonSetupResults = setupAddons(Object.values(addons), workspace);
|
|
3056
|
+
return await applyAddons({
|
|
3057
|
+
addons,
|
|
3058
|
+
workspace,
|
|
3059
|
+
options: options$6,
|
|
3060
|
+
addonSetupResults
|
|
3061
|
+
});
|
|
3062
|
+
}
|
|
3063
|
+
async function applyAddons({ addons, workspace, addonSetupResults, options: options$6 }) {
|
|
3064
|
+
const filesToFormat = /* @__PURE__ */ new Set();
|
|
3065
|
+
const allPnpmBuildDependencies = [];
|
|
3066
|
+
const mapped = Object.entries(addons).map(([, addon]) => addon);
|
|
3067
|
+
const ordered = orderAddons(mapped, addonSetupResults);
|
|
3068
|
+
for (const addon of ordered) {
|
|
3069
|
+
workspace = await createWorkspace({
|
|
3070
|
+
...workspace,
|
|
3071
|
+
options: options$6[addon.id]
|
|
3072
|
+
});
|
|
3073
|
+
const { files, pnpmBuildDependencies } = await runAddon({
|
|
3074
|
+
workspace,
|
|
3075
|
+
addon,
|
|
3076
|
+
multiple: ordered.length > 1
|
|
3077
|
+
});
|
|
3078
|
+
files.forEach((f) => filesToFormat.add(f));
|
|
3079
|
+
pnpmBuildDependencies.forEach((s) => allPnpmBuildDependencies.push(s));
|
|
3080
|
+
}
|
|
3081
|
+
return {
|
|
3082
|
+
filesToFormat: Array.from(filesToFormat),
|
|
3083
|
+
pnpmBuildDependencies: allPnpmBuildDependencies
|
|
3084
|
+
};
|
|
3085
|
+
}
|
|
3086
|
+
function setupAddons(addons, workspace) {
|
|
3087
|
+
const addonSetupResults = {};
|
|
3088
|
+
for (const addon of addons) {
|
|
3089
|
+
const setupResult = {
|
|
3090
|
+
unsupported: [],
|
|
3091
|
+
dependsOn: [],
|
|
3092
|
+
runsAfter: []
|
|
3093
|
+
};
|
|
3094
|
+
addon.setup?.({
|
|
3095
|
+
...workspace,
|
|
3096
|
+
dependsOn: (name) => {
|
|
3097
|
+
setupResult.dependsOn.push(name);
|
|
3098
|
+
setupResult.runsAfter.push(name);
|
|
3099
|
+
},
|
|
3100
|
+
unsupported: (reason) => setupResult.unsupported.push(reason),
|
|
3101
|
+
runsAfter: (name) => setupResult.runsAfter.push(name)
|
|
3102
|
+
});
|
|
3103
|
+
addonSetupResults[addon.id] = setupResult;
|
|
3104
|
+
}
|
|
3105
|
+
return addonSetupResults;
|
|
3106
|
+
}
|
|
3107
|
+
async function runAddon({ addon, multiple, workspace }) {
|
|
3108
|
+
const files = /* @__PURE__ */ new Set();
|
|
3109
|
+
for (const [id, question] of Object.entries(addon.options)) if (question.condition?.(workspace.options) !== false) workspace.options[id] ??= question.default;
|
|
3110
|
+
const dependencies = [];
|
|
3111
|
+
const pnpmBuildDependencies = [];
|
|
3112
|
+
const sv = {
|
|
3113
|
+
file: (path$1, content) => {
|
|
3114
|
+
try {
|
|
3115
|
+
let fileContent = fileExists(workspace.cwd, path$1) ? readFile(workspace.cwd, path$1) : "";
|
|
3116
|
+
fileContent = content(fileContent);
|
|
3117
|
+
if (!fileContent) return fileContent;
|
|
3118
|
+
writeFile(workspace, path$1, fileContent);
|
|
3119
|
+
files.add(path$1);
|
|
3120
|
+
} catch (e) {
|
|
3121
|
+
if (e instanceof Error) throw new Error(`Unable to process '${path$1}'. Reason: ${e.message}`);
|
|
3122
|
+
throw e;
|
|
3123
|
+
}
|
|
3124
|
+
},
|
|
3125
|
+
execute: async (commandArgs, stdio) => {
|
|
3126
|
+
const { command, args } = resolveCommand(workspace.packageManager, "execute", commandArgs);
|
|
3127
|
+
const addonPrefix = multiple ? `${addon.id}: ` : "";
|
|
3128
|
+
const executedCommand = `${command} ${args.join(" ")}`;
|
|
3129
|
+
if (!TESTING) T.step(`${addonPrefix}Running external command ${import_picocolors.default.gray(`(${executedCommand})`)}`);
|
|
3130
|
+
if (workspace.packageManager === "npm") args.unshift("--yes");
|
|
3131
|
+
try {
|
|
3132
|
+
await be(command, args, {
|
|
3133
|
+
nodeOptions: {
|
|
3134
|
+
cwd: workspace.cwd,
|
|
3135
|
+
stdio: TESTING ? "pipe" : stdio
|
|
3136
|
+
},
|
|
3137
|
+
throwOnError: true
|
|
3138
|
+
});
|
|
3139
|
+
} catch (error) {
|
|
3140
|
+
const typedError = error;
|
|
3141
|
+
throw new Error(`Failed to execute scripts '${executedCommand}': ${typedError.message}`, { cause: typedError.output });
|
|
3142
|
+
}
|
|
3143
|
+
},
|
|
3144
|
+
dependency: (pkg, version) => {
|
|
3145
|
+
dependencies.push({
|
|
3146
|
+
pkg,
|
|
3147
|
+
version,
|
|
3148
|
+
dev: false
|
|
3149
|
+
});
|
|
3150
|
+
},
|
|
3151
|
+
devDependency: (pkg, version) => {
|
|
3152
|
+
dependencies.push({
|
|
3153
|
+
pkg,
|
|
3154
|
+
version,
|
|
3155
|
+
dev: true
|
|
3156
|
+
});
|
|
3157
|
+
},
|
|
3158
|
+
pnpmBuildDependency: (pkg) => {
|
|
3159
|
+
pnpmBuildDependencies.push(pkg);
|
|
3160
|
+
}
|
|
3161
|
+
};
|
|
3162
|
+
await addon.run({
|
|
3163
|
+
...workspace,
|
|
3164
|
+
sv
|
|
3165
|
+
});
|
|
3166
|
+
const pkgPath = installPackages(dependencies, workspace);
|
|
3167
|
+
files.add(pkgPath);
|
|
3168
|
+
return {
|
|
3169
|
+
files: Array.from(files),
|
|
3170
|
+
pnpmBuildDependencies
|
|
3171
|
+
};
|
|
3172
|
+
}
|
|
3173
|
+
function orderAddons(addons, setupResults) {
|
|
3174
|
+
return addons.sort((a, b) => {
|
|
3175
|
+
return setupResults[a.id]?.runsAfter?.length - setupResults[b.id]?.runsAfter?.length;
|
|
3176
|
+
});
|
|
3177
|
+
}
|
|
3178
|
+
|
|
3179
|
+
//#endregion
|
|
3180
|
+
export { addDefault, applyAddons, communityAddonIds, create, createDefault, createWorkspace, formatFiles, getAddonDetails, getCommunityAddon, getHighlighter, installAddon, isVersionUnsupportedBelow, officialAddons, overrideProperties, setupAddons };
|