create-warlock 5.33.0 → 5.34.1

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.
Files changed (35) hide show
  1. package/CHANGELOG.md +22 -47
  2. package/README.md +69 -75
  3. package/esm/commands/create-new-app/index.mjs +10 -9
  4. package/esm/commands/create-new-app/index.mjs.map +1 -1
  5. package/esm/commands/create-new-app/types.d.mts +1 -1
  6. package/esm/commands/create-warlock-app/index.mjs +1 -1
  7. package/esm/commands/create-warlock-app/index.mjs.map +1 -1
  8. package/esm/flags/definitions.mjs +2 -2
  9. package/esm/flags/definitions.mjs.map +1 -1
  10. package/esm/flags/seed-from-flags.mjs +1 -1
  11. package/esm/flags/seed-from-flags.mjs.map +1 -1
  12. package/esm/helpers/app.mjs +4 -28
  13. package/esm/helpers/app.mjs.map +1 -1
  14. package/esm/helpers/insert-import.mjs +2 -2
  15. package/esm/helpers/insert-import.mjs.map +1 -1
  16. package/llms-full.txt +55 -84
  17. package/package.json +2 -2
  18. package/skills/create-a-warlock-project/SKILL.md +55 -84
  19. package/templates/toolchains/oxc/.oxlintrc.json +1 -1
  20. package/templates/warlock/{.prettierignore → .oxfmtignore} +0 -2
  21. package/templates/warlock/.oxfmtrc.json +15 -0
  22. package/templates/warlock/.oxlintrc.json +24 -0
  23. package/templates/warlock/.vscode/extensions.json +3 -0
  24. package/templates/warlock/.vscode/settings.json +11 -17
  25. package/templates/warlock/README.md +21 -22
  26. package/templates/warlock/package.json +7 -14
  27. package/templates/warlock/skills/code-standards/SKILL.md +53 -36
  28. package/templates/warlock/skills/git-workflow/SKILL.md +25 -20
  29. package/templates/warlock/skills/module-boundaries/SKILL.md +33 -24
  30. package/templates/warlock/skills/web-code-standards/SKILL.md +23 -20
  31. package/templates/warlock/src/web/home/components/home-footer.tsx +3 -1
  32. package/esm/prompts/ask-lint-toolchain.mjs +0 -21
  33. package/esm/prompts/ask-lint-toolchain.mjs.map +0 -1
  34. package/templates/warlock/.prettierrc.json +0 -12
  35. package/templates/warlock/eslint.config.js +0 -98
@@ -37,18 +37,18 @@ refactor(cart): extract totals calculation into a util
37
37
 
38
38
  ### 1.2 Types
39
39
 
40
- | Type | Use for |
41
- | ----------- | ------------------------------------------------ |
42
- | `feat` | New user-visible feature or capability |
43
- | `fix` | Bug fix |
44
- | `refactor` | Internal restructure with no behaviour change |
45
- | `docs` | Documentation only (skills, README, code docs) |
46
- | `test` | Adding or updating tests only |
47
- | `chore` | Tooling, config, scripts — no app code change |
48
- | `perf` | Performance improvement |
49
- | `build` | Build system or external dependencies |
50
- | `ci` | CI configuration |
51
- | `style` | Formatting only (rare — Prettier handles this) |
40
+ | Type | Use for |
41
+ | ---------- | ---------------------------------------------- |
42
+ | `feat` | New user-visible feature or capability |
43
+ | `fix` | Bug fix |
44
+ | `refactor` | Internal restructure with no behaviour change |
45
+ | `docs` | Documentation only (skills, README, code docs) |
46
+ | `test` | Adding or updating tests only |
47
+ | `chore` | Tooling, config, scripts — no app code change |
48
+ | `perf` | Performance improvement |
49
+ | `build` | Build system or external dependencies |
50
+ | `ci` | CI configuration |
51
+ | `style` | Formatting only (rare — Oxfmt handles this) |
52
52
 
53
53
  ### 1.3 Scope
54
54
 
@@ -143,18 +143,23 @@ Each PR is independently mergeable, independently reviewable.
143
143
 
144
144
  ```markdown
145
145
  ## What
146
+
146
147
  One-line summary of the change.
147
148
 
148
149
  ## Why
150
+
149
151
  The reason — link to a ticket, design doc, or bug report.
150
152
 
151
153
  ## How to verify
154
+
152
155
  Steps a reviewer can take to confirm it works.
153
156
 
154
157
  ## Screenshots / recordings
158
+
155
159
  If UI or output changed.
156
160
 
157
161
  ## Notes for reviewer
162
+
158
163
  Anything that's intentionally weird or worth knowing.
159
164
  ```
160
165
 
@@ -195,14 +200,14 @@ A drive-by approve is worse than no approve — it gives false confidence. If yo
195
200
 
196
201
  Every PR runs these. All must pass before merge. The exact commands match `package.json` scripts:
197
202
 
198
- | Gate | Command | Blocks on |
199
- | ----------------------------- | ----------------------------- | ---------------------------------- |
200
- | Type-check | `yarn tsc` | Any TS error |
201
- | Lint | `yarn lint` | Any ESLint error |
202
- | Tests | `yarn test --run` | Any failed test |
203
- | Coverage (recommended floor) | `yarn test:coverage` | TBD per team policy |
204
- | Dependency audit | `yarn audit --level=high` | High / critical vulnerabilities |
205
- | Format check | `yarn format --check` | Any unformatted file |
203
+ | Gate | Command | Blocks on |
204
+ | ---------------------------- | ------------------------- | ------------------------------- |
205
+ | Type-check | `yarn tsc` | Any TS error |
206
+ | Lint | `yarn lint` | Any Oxlint error |
207
+ | Tests | `yarn test --run` | Any failed test |
208
+ | Coverage (recommended floor) | `yarn test:coverage` | TBD per team policy |
209
+ | Dependency audit | `yarn audit --level=high` | High / critical vulnerabilities |
210
+ | Format check | `yarn format --check` | Any unformatted file |
206
211
 
207
212
  Additional rules:
208
213
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: module-boundaries
3
- description: 'Architecture rules for how modules under `src/app/**` relate — each module owns one domain noun (`orders`, `users`, `carts`, `payments`); standard layout per module (`controllers/`, `services/`, `models/<noun>/`, `repositories/`, `schema/`, `resources/`, `types/`, `utils/`, `events/`, `errors/`, `routes.ts`); public surface exposed through sub-folder barrels (`services/index.ts`, `utils/index.ts`, `types/index.ts`) — NEVER a module-root `index.ts` re-exporting everything; no module reaches into another module''s internal files; zero circular dependencies between modules; inter-module communication is explicit — direct service call OR `@mongez/events` bus, never shared mutable globals; cross-cutting helpers live in `src/app/shared/`. Triggers: adding a new module under `src/app/**`; importing from another module; deciding where new code belongs; circular import error; ESLint `import/no-restricted-paths` violation; user asks "can module A use module B''s internals", "how should modules talk", "where should this code live", "I''m getting a circular dependency", "what folders does a module need", "what goes in shared". Skip: intra-module file organization (load `skills/code-standards/SKILL.md` § 4); barrel mechanics within a module (load `skills/code-standards/SKILL.md` § 2.2); deploying / packaging / monorepo questions; framework primitive details — load relevant `@warlock.js/*` skill.'
3
+ description: 'Architecture rules for how modules under `src/app/**` relate — each module owns one domain noun (`orders`, `users`, `carts`, `payments`); standard layout per module (`controllers/`, `services/`, `models/<noun>/`, `repositories/`, `schema/`, `resources/`, `types/`, `utils/`, `events/`, `errors/`, `routes.ts`); public surface exposed through sub-folder barrels (`services/index.ts`, `utils/index.ts`, `types/index.ts`) — NEVER a module-root `index.ts` re-exporting everything; no module reaches into another module''s internal files; zero circular dependencies between modules; inter-module communication is explicit — direct service call OR `@mongez/events` bus, never shared mutable globals; cross-cutting helpers live in `src/app/shared/`. Triggers: adding a new module under `src/app/**`; importing from another module; deciding where new code belongs; circular import error; Oxlint `import/no-restricted-paths` violation; user asks "can module A use module B''s internals", "how should modules talk", "where should this code live", "I''m getting a circular dependency", "what folders does a module need", "what goes in shared". Skip: intra-module file organization (load `skills/code-standards/SKILL.md` § 4); barrel mechanics within a module (load `skills/code-standards/SKILL.md` § 2.2); deploying / packaging / monorepo questions; framework primitive details — load relevant `@warlock.js/*` skill.'
4
4
  ---
5
5
 
6
6
  # Module boundaries
@@ -116,26 +116,35 @@ See `skills/code-standards/SKILL.md` § 2.2 for the reasoning.
116
116
  - `src/app/<module>/schema/*` — schemas belong to the endpoint that uses them
117
117
  - `src/app/<module>/routes.ts` — registers routes for the framework, nobody imports it
118
118
 
119
- ### 3.3 Enforce with ESLint
119
+ ### 3.3 Enforce with Oxlint
120
120
 
121
- Add `eslint-plugin-import/no-restricted-paths` rules:
121
+ Add scoped native rules to `.oxlintrc.json`:
122
122
 
123
- ```js
124
- // eslint.config.js — proposed addition
123
+ ```json
125
124
  {
126
- rules: {
127
- "import/no-restricted-paths": ["error", {
128
- zones: [
129
- { target: "src/app/*/controllers", from: "src/app/*/controllers", except: ["./"] },
130
- { target: "src/app", from: "src/app/*/repositories", except: ["./*/services"] },
131
- { target: "src/app", from: "src/app/*/schema", except: ["./*/controllers"] },
132
- ],
133
- }],
134
- },
125
+ "overrides": [
126
+ {
127
+ "files": ["src/app/orders/**/*.ts"],
128
+ "rules": {
129
+ "no-restricted-imports": [
130
+ "error",
131
+ {
132
+ "patterns": [
133
+ {
134
+ "group": ["app/*/controllers/**", "app/*/repositories/**"],
135
+ "message": "Import public services instead"
136
+ }
137
+ ]
138
+ }
139
+ ]
140
+ }
141
+ }
142
+ ]
135
143
  }
136
144
  ```
137
145
 
138
- The exact rule shape depends on the eslint config — but the principle is: cross-module imports go through services / utils / types / resources / models / errors, never through the private folders.
146
+ Cross-module imports go through public services, utils, types, resources, models
147
+ and errors. Adapt each override to the module's public surface.
139
148
 
140
149
  ---
141
150
 
@@ -192,7 +201,7 @@ Or, if the shared concept is purely cross-cutting (not a domain), it goes to `ap
192
201
 
193
202
  ### 5.4 Detect in CI
194
203
 
195
- `eslint-plugin-import/no-cycle` catches cycles at lint time. Enable it.
204
+ `import/no-cycle` catches cycles at lint time. Enable it.
196
205
 
197
206
  ---
198
207
 
@@ -244,13 +253,13 @@ Top-level mutable exports across modules are the worst kind of coupling — invi
244
253
 
245
254
  ## 7. Where shared code lives
246
255
 
247
- | If the code is... | Put it in |
248
- | --------------------------------------- | --------------------------------------------- |
249
- | Used by 2+ modules, stable, cross-domain | `src/app/shared/` |
250
- | Used by 2+ modules, domain-specific | Extract a new domain module |
251
- | Used by exactly 1 module | Stays inside that module's `utils/` |
252
- | Third-party integration glue (SDK wrappers) | `src/app/shared/` (or a dedicated module) |
253
- | Build-time config | `src/config/` (one file per concern) |
256
+ | If the code is... | Put it in |
257
+ | ------------------------------------------- | ----------------------------------------- |
258
+ | Used by 2+ modules, stable, cross-domain | `src/app/shared/` |
259
+ | Used by 2+ modules, domain-specific | Extract a new domain module |
260
+ | Used by exactly 1 module | Stays inside that module's `utils/` |
261
+ | Third-party integration glue (SDK wrappers) | `src/app/shared/` (or a dedicated module) |
262
+ | Build-time config | `src/config/` (one file per concern) |
254
263
 
255
264
  ### 7.1 Promotion to `shared/` is a deliberate choice
256
265
 
@@ -285,7 +294,7 @@ Before merging a change that adds, restructures, or imports across modules:
285
294
  - [ ] No module-root `index.ts` created
286
295
  - [ ] Sub-folder barrels exist for `services/`, `utils/`, `types/` where applicable
287
296
  - [ ] No imports from another module's `controllers/`, `repositories/`, `schema/`
288
- - [ ] No circular imports between modules (ESLint `no-cycle` clean)
297
+ - [ ] No circular imports between modules (Oxlint `no-cycle` clean)
289
298
  - [ ] Cross-module communication uses direct service call or `@mongez/events` — never shared mutable globals
290
299
  - [ ] Shared code that's used by 2+ modules lives in `src/app/shared/`, not duplicated
291
300
  - [ ] Module has a `README.md` describing purpose, public surface, and ownership
@@ -53,19 +53,19 @@ make the feature harder to read.
53
53
 
54
54
  ## 1. Keep the page contract, make the implementation thin
55
55
 
56
- Warlock's file-routed modules have one policy export: `config`. A page's
57
- `route`, `cache`, `middleware`, `validation`, `metadata`, and `sitemap` all
58
- belong in that object; do not use legacy top-level policy exports. `loader`,
59
- `register`, `ErrorBoundary`, and the default component remain top-level
60
- exports. Layouts, registration files, `root.tsx`, `404.page.tsx`, and
61
- `error.page.tsx` also have framework-defined roles. These are intentional
62
- exceptions to the generic "one primary export per file" rule; do not split
63
- them merely to satisfy it.
56
+ Page logic lives in the matching `.setup.ts` file: `config`, `loader`,
57
+ `register`, and `action`/`actions`. UI files contain the default
58
+ component and named components such as `ErrorBoundary`. Pair `layout.tsx` with
59
+ `layout.setup.ts`, `root.tsx` with `root.setup.ts`, and `error.page.tsx` with
60
+ `error.setup.ts`. Use type-only imports from setup for loader/action inference.
61
+ The framework projects browser-safe `register()` code to the client. Put route,
62
+ cache, validation, middleware, metadata, and sitemap policy inside `config`;
63
+ metadata always belongs in `config.metadata`.
64
64
 
65
65
  Use the config type that matches the module. A page can preserve loader data
66
66
  inference for metadata by satisfying `PageConfig<typeof loader>`:
67
67
 
68
- ```tsx
68
+ ```ts title="src/web/posts.setup.ts"
69
69
  import type { PageConfig } from "@warlock.js/web";
70
70
 
71
71
  export async function loader() {
@@ -79,7 +79,9 @@ export const config = {
79
79
  metadata: ({ data }) => ({ title: data.title }),
80
80
  sitemap: { changefreq: "daily" },
81
81
  } satisfies PageConfig<typeof loader>;
82
+ ```
82
83
 
84
+ ```tsx title="src/web/posts.page.tsx"
83
85
  export default function PostsPage() {
84
86
  return <main>Posts</main>;
85
87
  }
@@ -93,21 +95,22 @@ route-name and parameter types are not available in 5.17.
93
95
  Layouts use one `config satisfies LayoutConfig` object for `prefix`,
94
96
  `middleware`, `metadata`, and a sitemap default. Roots use one
95
97
  `config satisfies RootConfig` object for `middleware` and the static `strictMode`
96
- flag: root modules do
97
- not declare routes, prefixes, metadata, validation, cache, or sitemap policy.
98
-
99
- ```tsx
100
- import type { LayoutConfig, RootConfig } from "@warlock.js/web";
98
+ flag, plus metadata defaults. Root modules do not declare routes, prefixes,
99
+ validation, cache, or sitemap policy.
101
100
 
102
- // src/web/account/layout.tsx
101
+ ```ts title="src/web/account/layout.setup.ts"
102
+ import type { LayoutConfig } from "@warlock.js/web";
103
103
  export const config = {
104
104
  prefix: "/account",
105
105
  middleware: [requireUser],
106
106
  metadata: { robots: "noindex" },
107
107
  sitemap: false,
108
108
  } satisfies LayoutConfig;
109
+ ```
110
+
111
+ ```ts title="src/web/root.setup.ts"
112
+ import type { RootConfig } from "@warlock.js/web";
109
113
 
110
- // src/web/root.tsx
111
114
  export const config = {
112
115
  middleware: [loadRequestContext],
113
116
  } satisfies RootConfig;
@@ -119,8 +122,8 @@ let TypeScript infer its return. Use
119
122
  `LayoutLoaderContext` for a layout, and `AppLoaderContext` for the root. Do not
120
123
  annotate the whole function as `PageLoader`, use `satisfies` on a loader,
121
124
  duplicate its payload shape, or cast an unknown return.
122
- A page file declares its route contract, coordinates a controller-like loader,
123
- and composes presentational components. The loader validates input, calls
125
+ A setup file declares the route contract and coordinates a controller-like
126
+ loader; its UI file composes presentational components. The loader validates input, calls
124
127
  named services under `src/app/<domain>/services/`, converts results through
125
128
  their resources, and returns the page data. **Every model/repository read and
126
129
  every business rule belongs in an app service, even when the loader would need
@@ -134,7 +137,7 @@ Extract substantive view sections into cohesive components. A component
134
137
  that needs any behavior gets a separate feature-named custom-hook file; a
135
138
  component with no behavior stays presentational and needs no hook. Split by
136
139
  responsibility, not by a mechanical line-count limit. Framework exports
137
- remain in the page file even when their work is delegated elsewhere.
140
+ for logic remain in the setup file even when their work is delegated elsewhere.
138
141
 
139
142
  Preserve the framework's rendering boundary: `root.tsx` owns the document,
140
143
  `#vessel` and `<Scripts />`; the loader runs on the server, while the rendered
@@ -292,7 +295,7 @@ and refresh primitives so state and loader data stay in sync.
292
295
 
293
296
  Apply the shared `code-standards` rules to web TypeScript as well: `type` for
294
297
  plain props/data, no `any`, no swallowed catches, no unsafe assertions used
295
- to hide an unknown data contract. Configure non-mutating ESLint and formatting
298
+ to hide an unknown data contract. Configure non-mutating Oxlint and formatting
296
299
  checks to enforce rules that can be automated; do not rely on a lint command
297
300
  that rewrites files as the only quality gate.
298
301
 
@@ -1,5 +1,7 @@
1
1
  import { HomeLogo } from "./logo";
2
2
 
3
+ const copyrightYear = new Date().getFullYear();
4
+
3
5
  export function HomeFooter() {
4
6
  return (
5
7
  <>
@@ -27,7 +29,7 @@ export function HomeFooter() {
27
29
  <a href="https://github.com/warlockjs" target="_blank" rel="noreferrer">
28
30
  GitHub
29
31
  </a>
30
- <span>MIT © {new Date().getFullYear()}</span>
32
+ <span>MIT © {copyrightYear}</span>
31
33
  </div>
32
34
  </footer>
33
35
  </>
@@ -1,21 +0,0 @@
1
- import { select } from "@clack/prompts";
2
-
3
- //#region ../../packages/create-warlock/src/prompts/ask-lint-toolchain.ts
4
- /** The full wizard offers the same toolchain choice as --lint. */
5
- function askLintToolchain(initialValue = "oxc") {
6
- return select({
7
- message: "Choose lint and formatting tools",
8
- options: [{
9
- value: "eslint",
10
- label: "ESLint + Prettier (deprecated)"
11
- }, {
12
- value: "oxc",
13
- label: "Oxlint + Oxfmt"
14
- }],
15
- initialValue
16
- });
17
- }
18
-
19
- //#endregion
20
- export { askLintToolchain };
21
- //# sourceMappingURL=ask-lint-toolchain.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ask-lint-toolchain.mjs","names":[],"sources":["../../../../../../../packages/create-warlock/src/prompts/ask-lint-toolchain.ts"],"sourcesContent":["import { select } from \"@clack/prompts\";\nimport type { LintToolchain } from \"../commands/create-new-app/types\";\n\n/** The full wizard offers the same toolchain choice as --lint. */\nexport function askLintToolchain(initialValue: LintToolchain = \"oxc\") {\n return select<LintToolchain>({\n message: \"Choose lint and formatting tools\",\n options: [\n { value: \"eslint\", label: \"ESLint + Prettier (deprecated)\" },\n { value: \"oxc\", label: \"Oxlint + Oxfmt\" },\n ],\n initialValue,\n });\n}\n"],"mappings":";;;;AAIA,SAAgB,iBAAiB,eAA8B,OAAO;CACpE,OAAO,OAAsB;EAC3B,SAAS;EACT,SAAS,CACP;GAAE,OAAO;GAAU,OAAO;EAAiC,GAC3D;GAAE,OAAO;GAAO,OAAO;EAAiB,CAC1C;EACA;CACF,CAAC;AACH"}
@@ -1,12 +0,0 @@
1
- {
2
- "semi": true,
3
- "tabWidth": 2,
4
- "printWidth": 100,
5
- "singleQuote": false,
6
- "arrowParens": "always",
7
- "trailingComma": "all",
8
- "endOfLine": "lf",
9
- "plugins": ["@ianvs/prettier-plugin-sort-imports"],
10
- "importOrder": ["<BUILTIN_MODULES>", "<THIRD_PARTY_MODULES>", "^(app|web|@shared)/", "^[.]"],
11
- "importOrderParserPlugins": ["typescript", "jsx", "decorators"]
12
- }
@@ -1,98 +0,0 @@
1
- import tsPlugin from "@typescript-eslint/eslint-plugin";
2
- import tsParser from "@typescript-eslint/parser";
3
- import prettierConfig from "eslint-config-prettier";
4
- import prettierPlugin from "eslint-plugin-prettier";
5
- import unusedImports from "eslint-plugin-unused-imports";
6
-
7
- export default [
8
- // Global configuration
9
- {
10
- languageOptions: {
11
- ecmaVersion: "latest",
12
- sourceType: "module",
13
- globals: {
14
- // Node.js globals
15
- console: "readonly",
16
- process: "readonly",
17
- Buffer: "readonly",
18
- __dirname: "readonly",
19
- __filename: "readonly",
20
- global: "readonly",
21
- module: "readonly",
22
- require: "readonly",
23
- exports: "readonly",
24
- // ES2021 globals
25
- Promise: "readonly",
26
- // Jest globals
27
- describe: "readonly",
28
- it: "readonly",
29
- test: "readonly",
30
- expect: "readonly",
31
- beforeEach: "readonly",
32
- afterEach: "readonly",
33
- beforeAll: "readonly",
34
- afterAll: "readonly",
35
- jest: "readonly",
36
- },
37
- },
38
- },
39
-
40
- // TypeScript files configuration
41
- {
42
- files: ["**/*.ts", "**/*.tsx"],
43
- languageOptions: {
44
- parser: tsParser,
45
- parserOptions: {
46
- ecmaVersion: "latest",
47
- sourceType: "module",
48
- },
49
- },
50
- plugins: {
51
- "@typescript-eslint": tsPlugin,
52
- prettier: prettierPlugin,
53
- "unused-imports": unusedImports,
54
- },
55
- rules: {
56
- // Prettier integration
57
- ...prettierConfig.rules,
58
- "prettier/prettier": "error",
59
-
60
- // TypeScript rules
61
- "@typescript-eslint/no-explicit-any": "off",
62
- "@typescript-eslint/no-unused-vars": "off",
63
- "no-unused-vars": "off",
64
- "unused-imports/no-unused-vars": [
65
- "warn",
66
- {
67
- vars: "all",
68
- varsIgnorePattern: "^_",
69
- args: "after-used",
70
- argsIgnorePattern: "^_",
71
- },
72
- ],
73
- "unused-imports/no-unused-imports": "error",
74
- "@typescript-eslint/explicit-member-accessibility": [
75
- "error",
76
- { accessibility: "explicit" },
77
- ],
78
- "@typescript-eslint/consistent-type-imports": [
79
- "error",
80
- {
81
- prefer: "type-imports",
82
- disallowTypeAnnotations: false,
83
- },
84
- ],
85
- },
86
- },
87
-
88
- // Ignore patterns
89
- {
90
- ignores: [
91
- "dist/",
92
- "node_modules/",
93
- "**/*.js",
94
- "!**/*.config.js",
95
- "!eslint.config.js",
96
- ],
97
- },
98
- ];