@icebreakers/monorepo-templates 0.1.11 → 0.1.13

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.
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env sh
2
2
  set -e
3
3
 
4
- pnpm exec lint-staged
4
+ pnpm exec nano-staged
package/assets/AGENTS.md CHANGED
@@ -17,13 +17,14 @@ This pnpm + Turbo monorepo keeps template sources under `templates/` (`cli`, `cl
17
17
 
18
18
  Follow the root `.editorconfig`: two-space indentation, LF line endings, UTF-8. Prefer TypeScript (`.ts`/`.tsx`) and Vue SFCs; name files with kebab-case (`user-table.vue`) and exported symbols with PascalCase for components or camelCase for utilities. ESLint (`@icebreakers/eslint-config`) and Stylelint enforce formatting; run `pnpm lint` before committing, and rely on Husky + lint-staged to auto-fix staged files. Any style-related code changes (CSS, SCSS, Less, Vue style blocks, and similar files) must pass Stylelint checks before commit. All changed code must pass ESLint checks before commit.
19
19
 
20
- `lint-staged.config.js` must include Stylelint checks for staged style files so style validation also runs during pre-commit.
20
+ `nano-staged.js` must include Stylelint checks for staged style files so style validation also runs during pre-commit.
21
21
 
22
22
  When a single code file exceeds 300 lines, treat it as a refactor signal and split it into smaller modules.
23
23
 
24
24
  When splitting code, do not create suffix-based sibling files such as `xxx.config.ts` or `xxx.filter.ts` just to move logic around. Create a dedicated folder (for example `xxx/`) and place the split modules inside it with clear responsibilities.
25
25
 
26
26
  All generated code must pass ESLint checks, and all generated style files must pass Stylelint checks.
27
+ Generated Vue files or other SFC files that include style blocks are also required to pass Stylelint checks.
27
28
 
28
29
  ## Testing Guidelines
29
30
 
@@ -3,14 +3,5 @@ import { icebreaker } from '@icebreakers/eslint-config'
3
3
  export default icebreaker(
4
4
  {
5
5
  ignores: ['**/fixtures/**'],
6
- formatters: {
7
- css: true,
8
- graphql: true,
9
- html: true,
10
- markdown: true,
11
- prettierOptions: {
12
- endOfLine: 'lf',
13
- },
14
- },
15
6
  },
16
7
  )
package/assets/gitignore CHANGED
@@ -35,4 +35,5 @@ yarn-error.log*
35
35
  dist
36
36
  vite.config.ts.timestamp-*.mjs
37
37
  config.ts.timestamp-*.mjs
38
- .vite-inspect
38
+ .vite-inspect
39
+ .playwright-cli
@@ -3,7 +3,7 @@
3
3
  "type": "module",
4
4
  "version": "0.0.0",
5
5
  "private": true,
6
- "packageManager": "pnpm@10.30.2",
6
+ "packageManager": "pnpm@10.31.0",
7
7
  "author": "ice breaker <hi@sonofmagic.top>",
8
8
  "repository": {
9
9
  "type": "git",
@@ -45,36 +45,33 @@
45
45
  "script:mirror": "monorepo mirror"
46
46
  },
47
47
  "devDependencies": {
48
- "@changesets/cli": "^2.29.8",
49
- "@commitlint/cli": "^20.4.2",
50
- "@icebreakers/changelog-github": "^0.2.0",
51
- "@icebreakers/commitlint-config": "^1.2.8",
52
- "@icebreakers/eslint-config": "^1.6.21",
48
+ "@changesets/cli": "^2.30.0",
49
+ "@commitlint/cli": "^20.4.3",
50
+ "@icebreakers/changelog-github": "^0.2.1",
51
+ "@icebreakers/commitlint-config": "^1.2.10",
52
+ "@icebreakers/eslint-config": "^1.6.27",
53
53
  "@icebreakers/monorepo": "workspace:*",
54
- "@icebreakers/stylelint-config": "^2.0.2",
55
- "@types/fs-extra": "^11.0.4",
56
- "@types/node": "^25.3.0",
54
+ "@icebreakers/stylelint-config": "^2.0.4",
55
+ "@types/node": "^25.3.5",
57
56
  "@types/semver": "^7.7.1",
58
57
  "@vitest/coverage-v8": "~4.0.18",
59
58
  "ci-info": "^4.4.0",
60
59
  "cross-env": "^10.1.0",
61
60
  "defu": "^6.1.4",
62
- "es-toolkit": "^1.44.0",
63
- "eslint": "^10.0.2",
64
- "execa": "^9.6.1",
65
- "fs-extra": "^11.3.3",
61
+ "es-toolkit": "^1.45.1",
62
+ "eslint": "^10.0.3",
66
63
  "husky": "^9.1.7",
67
- "lint-staged": "^16.2.7",
64
+ "nano-staged": "^0.9.0",
68
65
  "only-allow": "^1.2.2",
69
66
  "pathe": "^2.0.3",
70
67
  "pkg-types": "^2.3.0",
71
- "rimraf": "^6.1.3",
72
- "stylelint": "^17.3.0",
73
- "tsdown": "0.20.3",
68
+ "stylelint": "^17.4.0",
69
+ "tailwindcss": "^4.2.1",
70
+ "tsdown": "0.21.1",
74
71
  "tslib": "^2.8.1",
75
72
  "tsup": "^8.5.1",
76
73
  "tsx": "^4.21.0",
77
- "turbo": "^2.8.10",
74
+ "turbo": "^2.8.14",
78
75
  "type-fest": "^5.4.4",
79
76
  "typescript": "^5.9.3",
80
77
  "unbuild": "^3.6.1",
@@ -14,6 +14,10 @@ const CONFIG_FILENAMES = [
14
14
  'vitest.config.cjs',
15
15
  'vitest.config.mjs',
16
16
  ] as const
17
+ const WINDOWS_PATH_SEPARATOR_RE = /\\/g
18
+ const RELATIVE_CURRENT_DIR_RE = /^\.\//
19
+ const GLOB_TOKEN_RE = /[*?[{]/
20
+ const TRAILING_SLASH_RE = /\/+$/
17
21
 
18
22
  function extractBaseDirFromGlob(pattern: string): string | null {
19
23
  if (!pattern) {
@@ -21,14 +25,14 @@ function extractBaseDirFromGlob(pattern: string): string | null {
21
25
  }
22
26
 
23
27
  const normalized = pattern
24
- .replace(/\\/g, '/')
25
- .replace(/^\.\//, '')
26
- const globIndex = normalized.search(/[*?[{]/)
28
+ .replace(WINDOWS_PATH_SEPARATOR_RE, '/')
29
+ .replace(RELATIVE_CURRENT_DIR_RE, '')
30
+ const globIndex = normalized.search(GLOB_TOKEN_RE)
27
31
  const base = globIndex === -1
28
32
  ? normalized
29
33
  : normalized.slice(0, globIndex)
30
34
 
31
- const cleaned = base.replace(/\/+$/, '')
35
+ const cleaned = base.replace(TRAILING_SLASH_RE, '')
32
36
  return cleaned || null
33
37
  }
34
38
 
@@ -47,7 +51,7 @@ function loadProjectRootsFromWorkspace(): string[] {
47
51
  .map(extractBaseDirFromGlob)
48
52
  .filter((entry): entry is string => Boolean(entry))
49
53
 
50
- return roots.length ? Array.from(new Set(roots)) : []
54
+ return roots.length ? [...new Set(roots)] : []
51
55
  }
52
56
  catch (error) {
53
57
  console.warn('[vitest] Failed to parse pnpm-workspace.yaml, no project roots will be used.', error)
package/assets-data.mjs CHANGED
@@ -8,7 +8,7 @@ const coreAssetTargets = [
8
8
  '.npmrc',
9
9
  'commitlint.config.ts',
10
10
  'eslint.config.js',
11
- 'lint-staged.config.js',
11
+ 'nano-staged.js',
12
12
  'stylelint.config.js',
13
13
  'monorepo.config.ts',
14
14
  'package.json',
package/dist/index.d.mts CHANGED
@@ -1,4 +1,3 @@
1
- import { execaCommand } from "execa";
2
1
  import checkbox from "@inquirer/checkbox";
3
2
  import input from "@inquirer/input";
4
3
  import select from "@inquirer/select";
@@ -57,8 +56,14 @@ declare function scaffoldWorkspace(options: ScaffoldWorkspaceOptions): Promise<v
57
56
  //#region src/utils/command.d.ts
58
57
  interface RunCommandOptions {
59
58
  cwd?: string;
59
+ stdio?: 'inherit' | 'pipe';
60
60
  }
61
- declare function runCommand(command: string, args: string[], options?: RunCommandOptions): Promise<void>;
61
+ declare function runCommand(command: string, args: string[], options?: RunCommandOptions): Promise<{
62
+ exitCode: number | null;
63
+ }>;
64
+ declare function execaCommand(command: string, options?: RunCommandOptions): Promise<{
65
+ exitCode: number | null;
66
+ }>;
62
67
  //#endregion
63
68
  //#region src/utils/gitignore.d.ts
64
69
  declare function toPublishGitignorePath(input: string): string;
package/dist/index.mjs CHANGED
@@ -2,12 +2,11 @@ import * as path$1 from "node:path";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import fs, { access, open, rm } from "node:fs/promises";
5
- import { execa, execaCommand } from "execa";
5
+ import exec from "nanoexec";
6
6
  import checkbox from "@inquirer/checkbox";
7
7
  import input from "@inquirer/input";
8
8
  import select from "@inquirer/select";
9
9
  import { Command, program } from "commander";
10
-
11
10
  //#region assets-data.mjs
12
11
  const coreAssetTargets = [
13
12
  ".changeset",
@@ -19,7 +18,7 @@ const coreAssetTargets = [
19
18
  ".npmrc",
20
19
  "commitlint.config.ts",
21
20
  "eslint.config.js",
22
- "lint-staged.config.js",
21
+ "nano-staged.js",
23
22
  "stylelint.config.js",
24
23
  "monorepo.config.ts",
25
24
  "package.json",
@@ -44,7 +43,6 @@ const assetTargets$1 = [...coreAssetTargets, ...extraAssetTargets];
44
43
  function getAssetTargets$1(core = false) {
45
44
  return core ? [...coreAssetTargets] : [...assetTargets$1];
46
45
  }
47
-
48
46
  //#endregion
49
47
  //#region template-data.mjs
50
48
  const templateChoices$1 = [
@@ -97,18 +95,17 @@ const templateChoices$1 = [
97
95
  target: "apps/cli"
98
96
  }
99
97
  ];
100
-
101
98
  //#endregion
102
99
  //#region src/paths.ts
103
100
  const packageDir = path$1.resolve(fileURLToPath(new URL("../", import.meta.url)));
104
101
  const templatesDir = path$1.join(packageDir, "templates");
105
102
  const assetsDir = path$1.join(packageDir, "assets");
106
103
  const skeletonDir = assetsDir;
107
-
108
104
  //#endregion
109
105
  //#region src/utils/gitignore.ts
110
106
  const publishBasename = "gitignore";
111
107
  const workspaceBasename = ".gitignore";
108
+ const normalizedPathPattern = /[\\/]/;
112
109
  function detectSeparator(input) {
113
110
  if (input.includes("\\") && !input.includes("/")) return "\\";
114
111
  return "/";
@@ -116,10 +113,10 @@ function detectSeparator(input) {
116
113
  function replaceBasename(input, from, to) {
117
114
  if (!input) return input;
118
115
  const separator = detectSeparator(input);
119
- const normalized = input.replace(/[\\/]/g, separator);
116
+ const normalized = input.replace(normalizedPathPattern, separator);
120
117
  const hasTrailingSeparator = normalized.endsWith(separator);
121
118
  const segments = normalized.split(separator);
122
- if (hasTrailingSeparator && segments[segments.length - 1] === "") segments.pop();
119
+ if (hasTrailingSeparator && segments.at(-1) === "") segments.pop();
123
120
  const lastIndex = segments.length - 1;
124
121
  if (lastIndex >= 0 && segments[lastIndex] === from) {
125
122
  segments[lastIndex] = to;
@@ -137,7 +134,6 @@ function toWorkspaceGitignorePath(input) {
137
134
  function isGitignoreFile(name) {
138
135
  return toPublishGitignorePath(name) !== name || toWorkspaceGitignorePath(name) !== name;
139
136
  }
140
-
141
137
  //#endregion
142
138
  //#region src/utils/template-filter.ts
143
139
  const skipDirs = new Set([
@@ -153,7 +149,7 @@ const skipDirs = new Set([
153
149
  const skipFiles = new Set([
154
150
  ".DS_Store",
155
151
  "CHANGELOG.md",
156
- "typed-router.d.ts",
152
+ "route-map.d.ts",
157
153
  "worker-configuration.d.ts"
158
154
  ]);
159
155
  function shouldSkipTemplatePath(rootDir, targetPath) {
@@ -170,9 +166,9 @@ function shouldSkipTemplatePath(rootDir, targetPath) {
170
166
  function createTemplateCopyFilter(rootDir) {
171
167
  return (src) => !shouldSkipTemplatePath(rootDir, src);
172
168
  }
173
-
174
169
  //#endregion
175
170
  //#region src/prepare.ts
171
+ const huskySkippedEntryPattern = /[\\/]_$/;
176
172
  async function pathExists$1(targetPath) {
177
173
  try {
178
174
  await fs.access(targetPath);
@@ -232,7 +228,7 @@ async function copyAssets(repoRoot, overwriteExisting) {
232
228
  if (!await pathExists$1(from)) continue;
233
229
  const to = path$1.join(assetsDir, toPublishGitignorePath(target));
234
230
  const stats = await fs.stat(from);
235
- await copyEntry(from, to, overwriteExisting, target === ".husky" ? (src) => !/[\\/]_$/.test(src) : void 0);
231
+ await copyEntry(from, to, overwriteExisting, target === ".husky" ? (src) => !huskySkippedEntryPattern.test(src) : void 0);
236
232
  if (stats.isDirectory()) await renameGitignoreFiles(to);
237
233
  }
238
234
  }
@@ -255,7 +251,6 @@ async function prepareAssets(options = {}) {
255
251
  await copyAssets(repoRoot, overwriteExisting);
256
252
  await copyTemplates(repoRoot, overwriteExisting);
257
253
  }
258
-
259
254
  //#endregion
260
255
  //#region src/runtime-assets.ts
261
256
  const lockFileName = ".prepare-assets.lock";
@@ -319,7 +314,6 @@ async function ensureTemplateAssetsPrepared() {
319
314
  });
320
315
  await ensurePromise;
321
316
  }
322
-
323
317
  //#endregion
324
318
  //#region src/scaffold.ts
325
319
  async function isEmptyDir(dir) {
@@ -404,16 +398,25 @@ async function scaffoldWorkspace(options) {
404
398
  });
405
399
  }
406
400
  }
407
-
408
401
  //#endregion
409
402
  //#region src/utils/command.ts
410
403
  async function runCommand(command, args, options = {}) {
411
- await execa(command, args, {
412
- stdio: "inherit",
413
- ...options
404
+ const result = await exec(command, args, {
405
+ cwd: options.cwd,
406
+ stdio: options.stdio ?? "inherit"
414
407
  });
408
+ if (!result.ok) throw new Error(`Command failed with exit code ${result.code ?? "unknown"}: ${[command, ...args].join(" ")}`);
409
+ return { exitCode: result.code };
410
+ }
411
+ async function execaCommand(command, options = {}) {
412
+ const result = await exec(command, {
413
+ cwd: options.cwd,
414
+ shell: true,
415
+ stdio: options.stdio ?? "pipe"
416
+ });
417
+ if (!result.ok) throw new Error(`Command failed with exit code ${result.code ?? "unknown"}: ${command}`);
418
+ return { exitCode: result.code };
415
419
  }
416
-
417
420
  //#endregion
418
421
  //#region src/index.ts
419
422
  const templateChoices = templateChoices$1;
@@ -422,6 +425,5 @@ const getAssetTargets = getAssetTargets$1;
422
425
  const templateSourceMap = Object.fromEntries(templateChoices.map((item) => [item.key, item.source]));
423
426
  const templateTargetMap = Object.fromEntries(templateChoices.map((item) => [item.key, item.target]));
424
427
  const templateMap = templateSourceMap;
425
-
426
428
  //#endregion
427
- export { Command, assetTargets, assetsDir, checkbox, createTemplateCopyFilter, ensureTemplateAssetsPrepared, execaCommand, getAssetTargets, input, isGitignoreFile, packageDir, prepareAssets, program, runCommand, scaffoldTemplate, scaffoldWorkspace, select, shouldSkipTemplatePath, skeletonDir, templateChoices, templateMap, templateSourceMap, templateTargetMap, templatesDir, toPublishGitignorePath, toWorkspaceGitignorePath };
429
+ export { Command, assetTargets, assetsDir, checkbox, createTemplateCopyFilter, ensureTemplateAssetsPrepared, execaCommand, getAssetTargets, input, isGitignoreFile, packageDir, prepareAssets, program, runCommand, scaffoldTemplate, scaffoldWorkspace, select, shouldSkipTemplatePath, skeletonDir, templateChoices, templateMap, templateSourceMap, templateTargetMap, templatesDir, toPublishGitignorePath, toWorkspaceGitignorePath };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@icebreakers/monorepo-templates",
3
3
  "type": "module",
4
- "version": "0.1.11",
4
+ "version": "0.1.13",
5
5
  "description": "Template assets for icebreaker monorepo scaffolding",
6
6
  "author": "ice breaker <hi@sonofmagic.top>",
7
7
  "license": "MIT",
@@ -43,7 +43,7 @@
43
43
  "@inquirer/input": "^5.0.8",
44
44
  "@inquirer/select": "^5.1.0",
45
45
  "commander": "^14.0.3",
46
- "execa": "^9.6.1"
46
+ "nanoexec": "^1.1.0"
47
47
  },
48
48
  "publishConfig": {
49
49
  "access": "public"
@@ -22,7 +22,7 @@ dist-ssr
22
22
  *.njsproj
23
23
  *.sln
24
24
  *.sw?
25
- src/types/typed-router.d.ts
25
+ src/route-map.d.ts
26
26
  .wrangler
27
27
  .dev.vars*
28
- worker-configuration.d.ts
28
+ worker-configuration.d.ts
@@ -19,35 +19,34 @@
19
19
  "@tanstack/vue-query": "^5.92.9",
20
20
  "@tanstack/vue-query-devtools": "^6.1.5",
21
21
  "@tanstack/vue-table": "^8.21.3",
22
- "@tanstack/vue-virtual": "^3.13.19",
23
- "@trpc/client": "^11.10.0",
22
+ "@tanstack/vue-virtual": "^3.13.21",
23
+ "@trpc/client": "^11.12.0",
24
24
  "clsx": "^2.1.1",
25
25
  "pinia": "^3.0.4",
26
26
  "tailwind-merge": "^3.5.0",
27
27
  "tailwind-variants": "^3.2.2",
28
- "vue": "^3.5.29",
29
- "vue-i18n": "^11.2.8",
28
+ "vue": "^3.5.30",
29
+ "vue-i18n": "^11.3.0",
30
30
  "vue-router": "^5.0.3"
31
31
  },
32
32
  "devDependencies": {
33
- "@cloudflare/vite-plugin": "^1.25.5",
34
- "@hono/node-server": "^1.19.9",
33
+ "@cloudflare/vite-plugin": "^1.26.1",
34
+ "@hono/node-server": "^1.19.11",
35
35
  "@hono/trpc-server": "^0.4.2",
36
36
  "@tailwindcss/vite": "^4.2.1",
37
- "@trpc/server": "^11.10.0",
37
+ "@trpc/server": "^11.12.0",
38
38
  "@vitejs/plugin-vue": "^6.0.4",
39
39
  "@vitejs/plugin-vue-jsx": "^5.1.4",
40
- "@vue/tsconfig": "^0.8.1",
40
+ "@vue/tsconfig": "^0.9.0",
41
41
  "consola": "^3.4.2",
42
- "hono": "^4.12.2",
42
+ "hono": "^4.12.5",
43
43
  "tailwindcss": "^4.2.1",
44
44
  "typescript": "~5.9.3",
45
- "unplugin-vue-router": "^0.19.2",
46
45
  "vite": "^7.3.1",
47
- "vite-plugin-vue-devtools": "^8.0.6",
46
+ "vite-plugin-vue-devtools": "^8.0.7",
48
47
  "vite-tsconfig-paths": "^6.1.1",
49
48
  "vue-tsc": "3.2.5",
50
- "wrangler": "^4.68.1",
49
+ "wrangler": "^4.71.0",
51
50
  "zod": "^4.3.6"
52
51
  }
53
52
  }
@@ -1,2 +1 @@
1
1
  /// <reference types="vite/client" />
2
- /// <reference types="unplugin-vue-router/client" />
@@ -4,9 +4,9 @@ import { cloudflare } from '@cloudflare/vite-plugin'
4
4
  import Tailwindcss from '@tailwindcss/vite'
5
5
  import Vue from '@vitejs/plugin-vue'
6
6
  import VueJsx from '@vitejs/plugin-vue-jsx'
7
- import VueRouter from 'unplugin-vue-router/vite'
8
7
  import { defineConfig } from 'vite'
9
8
  import VueDevTools from 'vite-plugin-vue-devtools'
9
+ import VueRouter from 'vue-router/vite'
10
10
 
11
11
  const ensureVitePlugin = <T>(plugin: T) => plugin as unknown as PluginOption
12
12
  // https://vite.dev/config/
@@ -21,7 +21,7 @@ export default defineConfig({
21
21
  ensureVitePlugin(
22
22
  VueRouter(
23
23
  {
24
- dts: path.resolve(import.meta.dirname, 'src/types/typed-router.d.ts'),
24
+ dts: path.resolve(import.meta.dirname, 'src/route-map.d.ts'),
25
25
  },
26
26
  ),
27
27
  ),
@@ -52,10 +52,10 @@
52
52
  "types": "./dist/index.d.ts"
53
53
  },
54
54
  "devDependencies": {
55
- "@hono/node-server": "^1.19.9",
55
+ "@hono/node-server": "^1.19.11",
56
56
  "consola": "^3.4.2",
57
- "hono": "^4.12.2",
58
- "wrangler": "^4.68.1",
57
+ "hono": "^4.12.5",
58
+ "wrangler": "^4.71.0",
59
59
  "zod": "^4.3.6"
60
60
  }
61
61
  }
@@ -1,3 +1,5 @@
1
+ const lowercaseLetterPattern = /[a-z]/g
2
+
1
3
  export function format(str: string) {
2
- return str.replaceAll(/[a-z]/g, s => s.toUpperCase())
4
+ return str.replaceAll(lowercaseLetterPattern, s => s.toUpperCase())
3
5
  }
@@ -23,7 +23,7 @@
23
23
  "typecheck": "tsc -p tsconfig.json"
24
24
  },
25
25
  "dependencies": {
26
- "vue": "^3.5.29"
26
+ "vue": "^3.5.30"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@braintree/sanitize-url": "^7.1.2",
@@ -32,7 +32,7 @@
32
32
  "cytoscape-cose-bilkent": "^4.1.0",
33
33
  "dayjs": "^1.11.19",
34
34
  "debug": "^4.4.3",
35
- "mermaid": "^11.12.3",
35
+ "mermaid": "^11.13.0",
36
36
  "tailwindcss": "^4.2.1",
37
37
  "vitepress": "^1.6.4",
38
38
  "vitepress-plugin-mermaid": "^2.0.17"
@@ -46,11 +46,11 @@
46
46
  "devDependencies": {
47
47
  "@vitejs/plugin-vue": "^6.0.4",
48
48
  "@vue/test-utils": "^2.4.6",
49
- "@vue/tsconfig": "^0.8.1",
49
+ "@vue/tsconfig": "^0.9.0",
50
50
  "jsdom": "^28.1.0",
51
51
  "vite": "^7.3.1",
52
52
  "vite-plugin-dts": "^4.5.4",
53
- "vue": "^3.5.29",
53
+ "vue": "^3.5.30",
54
54
  "vue-tsc": "^3.2.5"
55
55
  }
56
56
  }