auto-svelte-pages-ts 1.2.2 → 2.2.2

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/CHANGELOG.md CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  All notable changes to `auto-svelte-pages-ts` will be documented in this file.
4
4
 
5
+
6
+ ## [2.2.2] - 2026-04-11
7
+
8
+ ### Added
9
+ - Config file support via `auto-svelte-pages.config.ts`.
10
+ - New `--config <file>` option to load config from a custom path.
11
+ - Configurable defaults for:
12
+ - directories (`src`, `entry`, `component`)
13
+ - Vite input markers (`start`, `end`)
14
+ - CSS import path for generated entry files
15
+
16
+ ### Changed
17
+ - Option priority is now: CLI args > config file > built-in defaults.
18
+ - TypeScript-first config discovery now uses:
19
+ - `auto-svelte-pages.config.ts` (default)
20
+ - fallback to `auto-svelte-pages.config.js` for backward compatibility.
21
+ - Updated CLI/help and README to reflect the new default config filename.
22
+
23
+
5
24
  ## [1.2.2] - 2026-04-10
6
25
 
7
26
  ### Dropped
package/README.md CHANGED
@@ -46,6 +46,8 @@ The CLI replaces only the content between those markers.
46
46
  - `--component-dir <dir>` component directory under src (default: `component`)
47
47
  - `--vite-config <file>` Vite config path from root (default: `vite.config.ts`)
48
48
  - `--css-import <path>` CSS import path for generated entry files (default: `../app.css`)
49
+ - `--config <file>` config file path (default: `auto-svelte-pages.config.ts`, fallback `.js`)
50
+ - `--watch` watch root HTML files and regenerate on changes
49
51
 
50
52
  ## Script Setup Example
51
53
 
@@ -59,6 +61,31 @@ The CLI replaces only the content between those markers.
59
61
  }
60
62
  ```
61
63
 
64
+ ## Config File
65
+
66
+ Create `auto-svelte-pages.config.ts` in project root:
67
+
68
+ ```ts
69
+ export default {
70
+ dirs: {
71
+ src: 'src',
72
+ entry: 'entry',
73
+ component: 'component',
74
+ },
75
+ cssImport: '../app.css',
76
+ markers: {
77
+ start: '// AUTO-GENERATED VITE INPUT START',
78
+ end: '// AUTO-GENERATED VITE INPUT END',
79
+ },
80
+ };
81
+ ```
82
+
83
+ Priority order:
84
+
85
+ - CLI args
86
+ - config file
87
+ - built-in defaults
88
+
62
89
 
63
90
  ## Use
64
91
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auto-svelte-pages-ts",
3
- "version": "1.2.2",
3
+ "version": "2.2.2",
4
4
  "description": "Generate Svelte TypeScript entry/component files from HTML pages and sync Vite multi-page inputs.",
5
5
  "type": "module",
6
6
  "files": [
package/src/cli.js CHANGED
@@ -59,6 +59,12 @@ function parseArgs(argv) {
59
59
  continue;
60
60
  }
61
61
 
62
+ if (arg === "--config") {
63
+ options.configPath = argv[i + 1];
64
+ i += 1;
65
+ continue;
66
+ }
67
+
62
68
  if (arg === "--help" || arg === "-h") {
63
69
  options.help = true;
64
70
  continue;
@@ -94,6 +100,7 @@ Options:
94
100
  --component-dir <dir>Component dir under src (default: component)
95
101
  --vite-config <file> Vite config path from root (default: vite.config.ts, fallback vite.config.js)
96
102
  --css-import <path> CSS import path used in generated entry file (default: ../app.css)
103
+ --config <file> Config file path (default: auto-svelte-pages.config.ts, fallback .js)
97
104
  --watch Watch root HTML files and regenerate on changes
98
105
  --help, -h Show help
99
106
 
package/src/generator.js CHANGED
@@ -1,8 +1,11 @@
1
1
  import { promises as fs } from "node:fs";
2
2
  import path from "node:path";
3
+ import { pathToFileURL } from "node:url";
3
4
 
4
- const INPUT_START_MARKER = "// AUTO-GENERATED VITE INPUT START";
5
- const INPUT_END_MARKER = "// AUTO-GENERATED VITE INPUT END";
5
+ const DEFAULT_INPUT_START_MARKER = "// AUTO-GENERATED VITE INPUT START";
6
+ const DEFAULT_INPUT_END_MARKER = "// AUTO-GENERATED VITE INPUT END";
7
+ const DEFAULT_CONFIG_TS_FILE = "auto-svelte-pages.config.ts";
8
+ const DEFAULT_CONFIG_JS_FILE = "auto-svelte-pages.config.js";
6
9
 
7
10
  function toPascalCase(value) {
8
11
  return value
@@ -22,13 +25,18 @@ function toViteInputKey(relativeHtmlPath) {
22
25
  return segments.join("_").toLowerCase();
23
26
  }
24
27
 
25
- function entryTemplate(componentName, appCssImportPath) {
28
+ function toImportPath(fromDir, toFile) {
29
+ const relativePath = normalizePath(path.relative(fromDir, toFile));
30
+ return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
31
+ }
32
+
33
+ function entryTemplate(componentName, appCssImportPath, componentImportPath) {
26
34
  return `import { mount } from 'svelte'
27
35
  import '${appCssImportPath}'
28
- import App from '../component/${componentName}.svelte'
36
+ import App from '${componentImportPath}'
29
37
 
30
38
  const app = mount(App, {
31
- target: document.getElementById('app'),
39
+ target: document.getElementById('app')!,
32
40
  })
33
41
 
34
42
  export default app
@@ -44,7 +52,10 @@ function componentTemplate(componentName) {
44
52
  `;
45
53
  }
46
54
 
47
- function htmlTemplate(pageName) {
55
+ function htmlTemplate(pageName, entryDirName) {
56
+ const entrySrc = normalizePath(
57
+ path.join("src", entryDirName, `${pageName}.ts`),
58
+ );
48
59
  return `<!DOCTYPE html>
49
60
  <html lang="en">
50
61
  <head>
@@ -54,7 +65,7 @@ function htmlTemplate(pageName) {
54
65
  </head>
55
66
  <body>
56
67
  <div id="app"></div>
57
- <script type="module" src="./src/entry/${pageName}.ts"></script>
68
+ <script type="module" src="./${entrySrc}"></script>
58
69
  </body>
59
70
  </html>
60
71
  `;
@@ -90,7 +101,7 @@ async function listHtmlFilesRecursively(rootDir, dir, ignoreDirs) {
90
101
  return htmlFiles;
91
102
  }
92
103
 
93
- function createInputBlock(relativeHtmlPaths) {
104
+ function createInputBlock(relativeHtmlPaths, markers) {
94
105
  const pathByKey = new Map();
95
106
  for (const relativePath of relativeHtmlPaths) {
96
107
  pathByKey.set(toViteInputKey(relativePath), normalizePath(relativePath));
@@ -100,11 +111,9 @@ function createInputBlock(relativeHtmlPaths) {
100
111
  .sort((a, b) => a[0].localeCompare(b[0]))
101
112
  .map(([key, relativePath]) => ` ${key}: '${relativePath}',`);
102
113
 
103
- return [
104
- ` ${INPUT_START_MARKER}`,
105
- ...lines,
106
- ` ${INPUT_END_MARKER}`,
107
- ].join("\n");
114
+ return [` ${markers.start}`, ...lines, ` ${markers.end}`].join(
115
+ "\n",
116
+ );
108
117
  }
109
118
 
110
119
  function parseExistingInputEntries(blockContent) {
@@ -141,6 +150,56 @@ async function resolveDefaultViteConfigPath(rootDir) {
141
150
  }
142
151
  }
143
152
 
153
+ async function loadConfig(rootDir, configPathFromCli) {
154
+ let candidatePath = null;
155
+ if (configPathFromCli) {
156
+ candidatePath = path.resolve(rootDir, configPathFromCli);
157
+ try {
158
+ await fs.access(candidatePath);
159
+ } catch {
160
+ return {};
161
+ }
162
+ } else {
163
+ const tsCandidate = path.join(rootDir, DEFAULT_CONFIG_TS_FILE);
164
+ const jsCandidate = path.join(rootDir, DEFAULT_CONFIG_JS_FILE);
165
+ try {
166
+ await fs.access(tsCandidate);
167
+ candidatePath = tsCandidate;
168
+ } catch {
169
+ try {
170
+ await fs.access(jsCandidate);
171
+ candidatePath = jsCandidate;
172
+ } catch {
173
+ return {};
174
+ }
175
+ }
176
+ }
177
+
178
+ const configModule = await import(pathToFileURL(candidatePath).href);
179
+ const loadedConfig = configModule.default ?? configModule;
180
+
181
+ if (
182
+ !loadedConfig ||
183
+ typeof loadedConfig !== "object" ||
184
+ Array.isArray(loadedConfig)
185
+ ) {
186
+ throw new Error(
187
+ `Invalid config file: ${path.relative(rootDir, candidatePath)}`,
188
+ );
189
+ }
190
+
191
+ return loadedConfig;
192
+ }
193
+
194
+ function resolveConfigValue(...values) {
195
+ for (const value of values) {
196
+ if (value !== undefined) {
197
+ return value;
198
+ }
199
+ }
200
+ return undefined;
201
+ }
202
+
144
203
  async function resolveRootTargetHtmlFiles(rootDir, targets) {
145
204
  const resolved = [];
146
205
  for (const target of targets) {
@@ -189,23 +248,25 @@ async function writeIfNeeded(filePath, content) {
189
248
  return "skipped";
190
249
  }
191
250
 
192
- async function writeHtmlBoilerplateIfNeeded(htmlPath, pageName) {
251
+ async function writeHtmlBoilerplateIfNeeded(htmlPath, pageName, entryDirName) {
193
252
  const existingContent = await fs.readFile(htmlPath, "utf8");
194
253
  if (existingContent.trim().length > 0) {
195
254
  return "skipped";
196
255
  }
197
256
 
198
- await fs.writeFile(htmlPath, htmlTemplate(pageName), "utf8");
257
+ await fs.writeFile(htmlPath, htmlTemplate(pageName, entryDirName), "utf8");
199
258
  return "templated";
200
259
  }
201
260
 
202
- async function updateViteInput({ rootDir, viteConfigPath, htmlFiles }) {
261
+ async function updateViteInput({
262
+ rootDir,
263
+ viteConfigPath,
264
+ htmlFiles,
265
+ markers,
266
+ }) {
203
267
  const viteContent = await fs.readFile(viteConfigPath, "utf8");
204
- const escapedStart = INPUT_START_MARKER.replace(
205
- /[.*+?^${}()|[\]\\]/g,
206
- "\\$&",
207
- );
208
- const escapedEnd = INPUT_END_MARKER.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
268
+ const escapedStart = markers.start.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
269
+ const escapedEnd = markers.end.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
209
270
  const markerPattern = getMarkerPattern(escapedStart, escapedEnd);
210
271
 
211
272
  if (!markerPattern.test(viteContent)) {
@@ -213,13 +274,13 @@ async function updateViteInput({ rootDir, viteConfigPath, htmlFiles }) {
213
274
  `Cannot update Vite input. Missing markers in ${path.relative(
214
275
  rootDir,
215
276
  viteConfigPath,
216
- )}: "${INPUT_START_MARKER}" and "${INPUT_END_MARKER}".`,
277
+ )}: "${markers.start}" and "${markers.end}".`,
217
278
  );
218
279
  }
219
280
 
220
281
  const nextContent = viteContent.replace(
221
282
  markerPattern,
222
- createInputBlock(htmlFiles),
283
+ createInputBlock(htmlFiles, markers),
223
284
  );
224
285
  await fs.writeFile(viteConfigPath, nextContent, "utf8");
225
286
  }
@@ -228,13 +289,11 @@ async function upsertViteInputTargets({
228
289
  rootDir,
229
290
  viteConfigPath,
230
291
  htmlTargets,
292
+ markers,
231
293
  }) {
232
294
  const viteContent = await fs.readFile(viteConfigPath, "utf8");
233
- const escapedStart = INPUT_START_MARKER.replace(
234
- /[.*+?^${}()|[\]\\]/g,
235
- "\\$&",
236
- );
237
- const escapedEnd = INPUT_END_MARKER.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
295
+ const escapedStart = markers.start.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
296
+ const escapedEnd = markers.end.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
238
297
  const markerPattern = getMarkerPattern(escapedStart, escapedEnd);
239
298
  const match = viteContent.match(markerPattern);
240
299
 
@@ -243,7 +302,7 @@ async function upsertViteInputTargets({
243
302
  `Cannot update Vite input. Missing markers in ${path.relative(
244
303
  rootDir,
245
304
  viteConfigPath,
246
- )}: "${INPUT_START_MARKER}" and "${INPUT_END_MARKER}".`,
305
+ )}: "${markers.start}" and "${markers.end}".`,
247
306
  );
248
307
  }
249
308
 
@@ -255,28 +314,90 @@ async function upsertViteInputTargets({
255
314
  const mergedPaths = [...existingEntries.values()];
256
315
  const nextContent = viteContent.replace(
257
316
  markerPattern,
258
- createInputBlock(mergedPaths),
317
+ createInputBlock(mergedPaths, markers),
259
318
  );
260
319
  await fs.writeFile(viteConfigPath, nextContent, "utf8");
261
320
  }
262
321
 
263
322
  export async function generatePages(userOptions = {}) {
264
323
  const rootDir = path.resolve(userOptions.rootDir ?? process.cwd());
265
- const srcDir = path.join(rootDir, userOptions.srcDir ?? "src");
266
- const entryDir = path.join(srcDir, userOptions.entryDir ?? "entry");
267
- const componentDir = path.join(
268
- srcDir,
269
- userOptions.componentDir ?? "component",
324
+ const loadedConfig = await loadConfig(rootDir, userOptions.configPath);
325
+
326
+ const srcDirName = resolveConfigValue(
327
+ userOptions.srcDir,
328
+ loadedConfig.srcDir,
329
+ loadedConfig.dirs?.src,
330
+ loadedConfig.dir?.src,
331
+ "src",
332
+ );
333
+ const entryDirName = resolveConfigValue(
334
+ userOptions.entryDir,
335
+ loadedConfig.entryDir,
336
+ loadedConfig.dirs?.entry,
337
+ loadedConfig.dir?.entry,
338
+ "entry",
339
+ );
340
+ const componentDirName = resolveConfigValue(
341
+ userOptions.componentDir,
342
+ loadedConfig.componentDir,
343
+ loadedConfig.dirs?.component,
344
+ loadedConfig.dir?.component,
345
+ "component",
270
346
  );
271
- const viteConfigPath = userOptions.viteConfig
272
- ? path.join(rootDir, userOptions.viteConfig)
347
+ const viteConfigFile = resolveConfigValue(
348
+ userOptions.viteConfig,
349
+ loadedConfig.viteConfig,
350
+ undefined,
351
+ );
352
+
353
+ const srcDir = path.join(rootDir, srcDirName);
354
+ const entryDir = path.join(srcDir, entryDirName);
355
+ const componentDir = path.join(srcDir, componentDirName);
356
+ const viteConfigPath = viteConfigFile
357
+ ? path.join(rootDir, viteConfigFile)
273
358
  : await resolveDefaultViteConfigPath(rootDir);
274
- const updateVite = userOptions.updateVite !== false;
275
- const includeNestedHtml = userOptions.includeNestedHtml !== false;
359
+
360
+ const updateVite =
361
+ resolveConfigValue(
362
+ userOptions.updateVite,
363
+ loadedConfig.updateVite,
364
+ true,
365
+ ) !== false;
366
+ const includeNestedHtml =
367
+ resolveConfigValue(
368
+ userOptions.includeNestedHtml,
369
+ loadedConfig.includeNestedHtml,
370
+ true,
371
+ ) !== false;
276
372
  const ignoreDirs = new Set(
277
- userOptions.ignoreDirs ?? [".git", "node_modules", "dist"],
373
+ resolveConfigValue(userOptions.ignoreDirs, loadedConfig.ignoreDirs, [
374
+ ".git",
375
+ "node_modules",
376
+ "dist",
377
+ ]),
378
+ );
379
+ const appCssImportPath = resolveConfigValue(
380
+ userOptions.appCssImportPath,
381
+ loadedConfig.appCssImportPath,
382
+ loadedConfig.cssImport,
383
+ "../app.css",
278
384
  );
279
- const appCssImportPath = userOptions.appCssImportPath ?? "../app.css";
385
+ const markers = {
386
+ start: resolveConfigValue(
387
+ userOptions.inputStartMarker,
388
+ loadedConfig.inputStartMarker,
389
+ loadedConfig.markers?.start,
390
+ loadedConfig.marker?.start,
391
+ DEFAULT_INPUT_START_MARKER,
392
+ ),
393
+ end: resolveConfigValue(
394
+ userOptions.inputEndMarker,
395
+ loadedConfig.inputEndMarker,
396
+ loadedConfig.markers?.end,
397
+ loadedConfig.marker?.end,
398
+ DEFAULT_INPUT_END_MARKER,
399
+ ),
400
+ };
280
401
  const targetFiles = Array.isArray(userOptions.targets)
281
402
  ? userOptions.targets
282
403
  : [];
@@ -301,15 +422,20 @@ export async function generatePages(userOptions = {}) {
301
422
  const htmlPath = path.join(rootDir, htmlFile);
302
423
  const entryPath = path.join(entryDir, `${baseName}.ts`);
303
424
  const componentPath = path.join(componentDir, `${componentName}.svelte`);
425
+ const componentImportPath = toImportPath(entryDir, componentPath);
304
426
 
305
- const htmlResult = await writeHtmlBoilerplateIfNeeded(htmlPath, baseName);
427
+ const htmlResult = await writeHtmlBoilerplateIfNeeded(
428
+ htmlPath,
429
+ baseName,
430
+ entryDirName,
431
+ );
306
432
  logs.push(
307
433
  `${htmlResult.toUpperCase()} ${path.relative(rootDir, htmlPath)}`,
308
434
  );
309
435
 
310
436
  const entryResult = await writeIfNeeded(
311
437
  entryPath,
312
- entryTemplate(componentName, appCssImportPath),
438
+ entryTemplate(componentName, appCssImportPath, componentImportPath),
313
439
  );
314
440
  logs.push(
315
441
  `${entryResult.toUpperCase()} ${path.relative(rootDir, entryPath)}`,
@@ -330,13 +456,14 @@ export async function generatePages(userOptions = {}) {
330
456
  rootDir,
331
457
  viteConfigPath,
332
458
  htmlTargets: rootHtmlFiles,
459
+ markers,
333
460
  });
334
461
  } else {
335
462
  const htmlFiles = includeNestedHtml
336
463
  ? await listHtmlFilesRecursively(rootDir, rootDir, ignoreDirs)
337
464
  : rootHtmlFiles;
338
465
  htmlFiles.sort((a, b) => a.localeCompare(b));
339
- await updateViteInput({ rootDir, viteConfigPath, htmlFiles });
466
+ await updateViteInput({ rootDir, viteConfigPath, htmlFiles, markers });
340
467
  }
341
468
  logs.push(`UPDATED ${path.relative(rootDir, viteConfigPath)} Vite input`);
342
469
  }
@@ -349,8 +476,8 @@ export async function generatePages(userOptions = {}) {
349
476
  rootHtmlFiles,
350
477
  logs,
351
478
  markers: {
352
- start: INPUT_START_MARKER,
353
- end: INPUT_END_MARKER,
479
+ start: markers.start,
480
+ end: markers.end,
354
481
  },
355
482
  };
356
483
  }