@orkestrel/scaffold 0.0.41 → 0.0.42

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.
@@ -102,8 +102,9 @@ Exported from `@orkestrel/scaffold`, and reachable from
102
102
  | `ENGINES_PATTERN` | const | The minimum-Node engine syntax a blueprint declares. |
103
103
  | `ENVIRONMENTS` | const | The three `Environment` values, frozen. |
104
104
  | `EXECUTABLE_PATHS` | const | The vendored paths a target receives with its executable bit set, frozen. |
105
- | `EXTRA_NAME_PATTERN` | const | The development extra name syntax: any valid npm package name. |
106
105
  | `EXTRA_RANGE_PATTERN` | const | The registry-only semver subset accepted for a development extra's range. |
106
+ | `FLOOR_RANGE_PATTERN` | const | The exact three-component floor accepted for a foreign peer's range. |
107
+ | `FOREIGN_NAME_PATTERN` | const | The package name syntax for a dependency this package does not publish. |
107
108
  | `GLOBAL_SETUP_PATH` | const | The shared Vitest global-setup module whose presence makes a workspace `global`. |
108
109
  | `GROUPS` | const | The seven `Group` values in plan order, frozen. |
109
110
  | `GUIDES_TEST_PATH` | const | The guide-parity proof whose presence selects the planned `guides` project. |
@@ -587,8 +588,14 @@ blueprint.engines // '>=22.12.0'
587
588
 
588
589
  `src` selects published library environments and `app` selects private application environments.
589
590
  The two axes are independent, so a library-only, an application-only, and a mixed workspace are all
590
- first class. `dependencies` and `peers` are runtime `@orkestrel/*` packages; `extras` are
591
- development dependencies and may carry any valid npm name.
591
+ first class. `dependencies` are runtime `@orkestrel/*` packages. A peer in the `@orkestrel` scope is
592
+ a fleet pin; every other peer is a floor. `extras` are development dependencies and may carry any
593
+ valid npm name. A peer reaches the generated workspace through two representations:
594
+ `Blueprint.peers` validates the scope rule and compiles the manifest declarations, while the
595
+ `peers` binding in the generated `vite.config.ts` derives from the target's live
596
+ `peerDependencies`. Each published build face — core, browser, server, and `bin` — externalizes
597
+ every name in that binding, so a peer the workspace declares by hand, such as `vitest`, is left as
598
+ an import in the emitted bundle rather than inlined into it.
592
599
 
593
600
  One published environment owns the package root directly. Several published environments require
594
601
  `core`, which owns that root while each other environment keeps its subpath. A multi-environment
File without changes
File without changes
File without changes
File without changes
@@ -12,6 +12,7 @@ import {
12
12
  rmSync,
13
13
  writeFileSync,
14
14
  } from 'node:fs'
15
+ import { createRequire } from 'node:module'
15
16
  import { dirname, join, resolve } from 'node:path'
16
17
  import { fileURLToPath, pathToFileURL } from 'node:url'
17
18
  import { build, loadConfigFromFile } from 'vite'
@@ -588,16 +589,37 @@ describe('policy plugin', () => {
588
589
  ].join('\n'),
589
590
  )
590
591
 
591
- const binary = resolve(root, 'node_modules/.bin/oxlint')
592
+ // Run oxlint's real Node entry through the current interpreter rather than the
593
+ // `node_modules/.bin/oxlint` shim. That shim is a POSIX `sh` script — a symlink to one on
594
+ // Linux, a `.cmd`/`.ps1` pair on Windows — and Windows `CreateProcess` cannot execute the
595
+ // extensionless form; spawning the `.cmd` would need `shell: true`, which breaks on paths
596
+ // containing spaces. Resolving through `createRequire` reads oxlint's own `bin` field, so
597
+ // the entry survives hoisting, a nested `node_modules` layout, and a future rename.
598
+ const manifestPath = createRequire(join(root, 'package.json')).resolve('oxlint/package.json')
599
+ const manifest: unknown = JSON.parse(readFileSync(manifestPath, 'utf8'))
600
+ if (typeof manifest !== 'object' || manifest === null) {
601
+ throw new Error('The oxlint package manifest is not an object')
602
+ }
603
+ const bin: unknown = Object.getOwnPropertyDescriptor(manifest, 'bin')?.value
604
+ const entry: unknown =
605
+ typeof bin === 'string'
606
+ ? bin
607
+ : typeof bin === 'object' && bin !== null
608
+ ? Object.getOwnPropertyDescriptor(bin, 'oxlint')?.value
609
+ : undefined
610
+ if (typeof entry !== 'string') {
611
+ throw new Error('The oxlint package declares no bin.oxlint entry')
612
+ }
613
+ const binary = resolve(dirname(manifestPath), entry)
592
614
  const config = resolve(root, '.oxlintrc.json')
593
615
  const violations = spawnSync(
594
- binary,
595
- ['--config', config, '--format', 'json', resolve(scratch.path, 'violations')],
616
+ process.execPath,
617
+ [binary, '--config', config, '--format', 'json', resolve(scratch.path, 'violations')],
596
618
  { cwd: root, encoding: 'utf8', timeout: 15_000 },
597
619
  )
598
620
  const clean = spawnSync(
599
- binary,
600
- ['--config', config, '--format', 'json', resolve(scratch.path, 'clean')],
621
+ process.execPath,
622
+ [binary, '--config', config, '--format', 'json', resolve(scratch.path, 'clean')],
601
623
  { cwd: root, encoding: 'utf8', timeout: 15_000 },
602
624
  )
603
625
  const reports: string[][] = []
@@ -235,16 +235,16 @@ var NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
235
235
  */
236
236
  var DEPENDENCY_NAME_PATTERN = /^@orkestrel\/[a-z][a-z0-9-]*$/;
237
237
  /**
238
- * The development extra name syntax: any valid npm package name.
238
+ * The package name syntax for a dependency this package does not publish.
239
239
  *
240
240
  * @remarks
241
- * Wider than a runtime dependency name on purpose. An extra is manifest content
242
- * and never reaches a path, so it may carry any scope or no scope at all. The
243
- * single optional `/` is still fixed to the one scope boundary, and neither `..`
244
- * nor a backslash is admitted, so the shape cannot express a traversal even
245
- * though it accepts far more names.
241
+ * A foreign package is one this package does not publish, so its name reaches
242
+ * no path. It may carry any scope or no scope at all. Each name segment begins
243
+ * with an alphanumeric character after an optional leading `@`, and the name
244
+ * carries at most one `/`. No segment can therefore be `..`, and no backslash
245
+ * is admitted, so the shape cannot express a traversal.
246
246
  */
247
- var EXTRA_NAME_PATTERN = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
247
+ var FOREIGN_NAME_PATTERN = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
248
248
  /** The exact three-component version syntax a blueprint declares. */
249
249
  var VERSION_PATTERN = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/;
250
250
  /**
@@ -259,6 +259,15 @@ var VERSION_PATTERN = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/;
259
259
  var ORKESTREL_RANGE_PATTERN = /^\^0\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/;
260
260
  /** The registry-only semver subset accepted for a development extra's range. */
261
261
  var EXTRA_RANGE_PATTERN = /^(?:\^|~)?(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*)?$/;
262
+ /**
263
+ * The exact three-component floor accepted for a foreign peer's range.
264
+ *
265
+ * @remarks
266
+ * This is independent from {@link ENGINES_PATTERN}. An engine floors the Node
267
+ * runtime a workspace supports, while a peer floors a tool the consumer
268
+ * supplies. Either obligation may change without changing the other.
269
+ */
270
+ var FLOOR_RANGE_PATTERN = /^>=(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/;
262
271
  /** The minimum-Node engine syntax a blueprint declares. */
263
272
  var ENGINES_PATTERN = /^>=(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/;
264
273
  /** Exact lowercase hexadecimal bytes: two digits per byte, and empty content is valid. */
@@ -3017,7 +3026,10 @@ function srcToExports(src) {
3017
3026
  * each declared axis adds the set its host needs. A declared extra or peer is
3018
3027
  * applied after those, so a workspace that pins a range for a package the
3019
3028
  * conditional sets also name gets the range it declared. An extra that restates
3020
- * a shared toolchain pin never reaches here, because the gate refuses it.
3029
+ * a shared toolchain pin never reaches here, because the gate refuses it. A peer
3030
+ * that names a development tool already selected keeps that tool's pin, because
3031
+ * the peer's floor states what consumers may supply rather than what this workspace
3032
+ * develops against.
3021
3033
  *
3022
3034
  * A peer is declared here as well as under `peerDependencies`, because a peer is
3023
3035
  * not installed by the workspace that declares it and developing against one
@@ -3050,7 +3062,7 @@ function blueprintToDevDependencies(blueprint) {
3050
3062
  ...blueprint.app.includes("server") ? APP_SERVER_DEV_DEPENDENCIES : {}
3051
3063
  };
3052
3064
  for (const extra of blueprint.extras) merged[extra.name] = extra.range;
3053
- for (const peer of blueprint.peers) merged[peer.name] = peer.range;
3065
+ for (const peer of blueprint.peers) if (!Object.hasOwn(merged, peer.name)) merged[peer.name] = peer.range;
3054
3066
  const own = blueprint.src.length > 0 ? `@orkestrel/${blueprint.name}` : blueprint.name;
3055
3067
  const runtime = new Set(blueprint.dependencies.map((dependency) => dependency.name));
3056
3068
  return Object.fromEntries(Object.entries(merged).filter(([name]) => name !== own && !runtime.has(name)).sort(([left], [right]) => (0, _orkestrel_contract.compareValues)(left, right)));
@@ -4139,11 +4151,11 @@ function planToFindings(plan, current) {
4139
4151
  * range, in list order.
4140
4152
  *
4141
4153
  * @remarks
4142
- * The three declared lists differ only in the two syntaxes they accept, so the
4143
- * rules live here once and each caller supplies its own patterns. A runtime
4144
- * dependency name reaches a path through its guide mirror and is fixed to the
4145
- * `@orkestrel` scope; a development extra never reaches a path and admits any
4146
- * valid npm name.
4154
+ * The declared lists and peer partitions differ only in the two syntaxes they
4155
+ * accept, so the rules live here once and each caller supplies its own patterns.
4156
+ * A runtime dependency name reaches a path through its guide mirror and is
4157
+ * fixed to the `@orkestrel` scope. A foreign peer or development extra reaches
4158
+ * no path and admits any valid npm name.
4147
4159
  *
4148
4160
  * Both patterns must be stateless. A global or sticky pattern carries a
4149
4161
  * `lastIndex` between calls, so it would answer differently for the same input
@@ -4301,7 +4313,7 @@ function blueprintToQuestions(blueprint) {
4301
4313
  message: "showcase projects a browser app, and this workspace declares none, so it emits nothing.",
4302
4314
  blocking: false
4303
4315
  });
4304
- questions.push(...dependenciesToQuestions(blueprint.dependencies, "dependencies", DEPENDENCY_NAME_PATTERN, ORKESTREL_RANGE_PATTERN), ...dependenciesToQuestions(blueprint.peers, "peers", DEPENDENCY_NAME_PATTERN, ORKESTREL_RANGE_PATTERN), ...dependenciesToQuestions(blueprint.extras, "extras", EXTRA_NAME_PATTERN, EXTRA_RANGE_PATTERN));
4316
+ questions.push(...dependenciesToQuestions(blueprint.dependencies, "dependencies", DEPENDENCY_NAME_PATTERN, ORKESTREL_RANGE_PATTERN), ...dependenciesToQuestions(blueprint.peers.filter((peer) => peer.name.startsWith("@orkestrel/")), "peers", DEPENDENCY_NAME_PATTERN, ORKESTREL_RANGE_PATTERN), ...dependenciesToQuestions(blueprint.peers.filter((peer) => !peer.name.startsWith("@orkestrel/")), "peers", FOREIGN_NAME_PATTERN, FLOOR_RANGE_PATTERN), ...dependenciesToQuestions(blueprint.extras, "extras", FOREIGN_NAME_PATTERN, EXTRA_RANGE_PATTERN));
4305
4317
  const lists = [
4306
4318
  ["dependencies", blueprint.dependencies],
4307
4319
  ["peers", blueprint.peers],
@@ -4857,8 +4869,9 @@ exports.DISTRIBUTION_TEST_PATH = DISTRIBUTION_TEST_PATH;
4857
4869
  exports.ENGINES_PATTERN = ENGINES_PATTERN;
4858
4870
  exports.ENVIRONMENTS = ENVIRONMENTS;
4859
4871
  exports.EXECUTABLE_PATHS = EXECUTABLE_PATHS;
4860
- exports.EXTRA_NAME_PATTERN = EXTRA_NAME_PATTERN;
4861
4872
  exports.EXTRA_RANGE_PATTERN = EXTRA_RANGE_PATTERN;
4873
+ exports.FLOOR_RANGE_PATTERN = FLOOR_RANGE_PATTERN;
4874
+ exports.FOREIGN_NAME_PATTERN = FOREIGN_NAME_PATTERN;
4862
4875
  exports.GLOBAL_SETUP_PATH = GLOBAL_SETUP_PATH;
4863
4876
  exports.GROUPS = GROUPS;
4864
4877
  exports.GUIDES_TEST_PATH = GUIDES_TEST_PATH;