@orkestrel/scaffold 0.0.50 → 0.0.51

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.
@@ -3,7 +3,7 @@ import { fillTemplate } from "@orkestrel/template";
3
3
  import { Emitter } from "@orkestrel/emitter";
4
4
  var package_default = {
5
5
  name: "@orkestrel/scaffold",
6
- version: "0.0.50",
6
+ version: "0.0.51",
7
7
  description: "Scaffold workspaces with five commands: new, audit, repair, catalog, and overwrite.",
8
8
  keywords: [
9
9
  "audit",
@@ -560,7 +560,7 @@ var CONFIG_TEMPLATES = Object.freeze({
560
560
  }
561
561
  `,
562
562
  vite: `import type { {{viteTypes}} } from 'vite'
563
- {{imports}}import { defineConfig, mergeConfig } from 'vitest/config'
563
+ {{imports}}import { defineConfig } from 'vitest/config'
564
564
  import manifest from './package.json' with { type: 'json' }
565
565
  import tsconfig from './tsconfig.json' with { type: 'json' }
566
566
  {{helpers}}{{browsers}}import { fileURLToPath, URL } from 'node:url'
@@ -599,160 +599,140 @@ const resolve = {
599
599
  }),
600
600
  factories: Object.freeze({
601
601
  src: Object.freeze({
602
- core: `export const srcCore = (options?: UserConfig): UserConfig =>
603
- mergeConfig(
604
- {
605
- resolve,
606
- publicDir: false,
607
- build: {
608
- emptyOutDir: true,
609
- sourcemap: true,
610
- minify: false,
611
- rolldownOptions: { onLog: enforceBuildLog },
612
- },
613
- test: {
614
- name: { label: 'src:core', color: 'magenta' },
615
- include: ['tests/src/core/**/*.test.ts'],
616
- setupFiles: ['./tests/setup.ts'],
617
- environment: 'node',
618
- browser: { enabled: false },
619
- },
620
- },
621
- options ?? {},
622
- )
602
+ core: `export const srcCore = (): UserConfig => ({
603
+ resolve,
604
+ publicDir: false,
605
+ build: {
606
+ emptyOutDir: true,
607
+ sourcemap: true,
608
+ minify: false,
609
+ rolldownOptions: { onLog: enforceBuildLog },
610
+ },
611
+ test: {
612
+ name: { label: 'src:core', color: 'magenta' },
613
+ include: ['tests/src/core/**/*.test.ts'],
614
+ setupFiles: ['./tests/setup.ts'],
615
+ environment: 'node',
616
+ browser: { enabled: false },
617
+ },
618
+ })
623
619
  `,
624
- browser: `export const srcBrowser = (options?: UserConfig): UserConfig =>
625
- mergeConfig(
626
- {
627
- resolve,
628
- publicDir: false,
629
- plugins: [outputBoundary('dist/src/browser'), environmentBoundary('src/browser')],
630
- build: {
631
- emptyOutDir: true,
632
- sourcemap: true,
633
- minify: false,
634
- lib: {
635
- entry: resolveWorkspacePath('src/browser/index.ts'),
636
- formats: ['es'],
637
- fileName: () => 'index.js',
638
- },
639
- outDir: 'dist/src/browser',
640
- rolldownOptions: {
641
- onLog: enforceBuildLog,
642
- {{external}}
620
+ browser: `export const srcBrowser = (): UserConfig => ({
621
+ resolve,
622
+ publicDir: false,
623
+ plugins: [outputBoundary('dist/src/browser'), environmentBoundary('src/browser')],
624
+ build: {
625
+ emptyOutDir: true,
626
+ sourcemap: true,
627
+ minify: false,
628
+ lib: {
629
+ entry: resolveWorkspacePath('src/browser/index.ts'),
630
+ formats: ['es'],
631
+ fileName: () => 'index.js',
632
+ },
633
+ outDir: 'dist/src/browser',
634
+ rolldownOptions: {
635
+ onLog: enforceBuildLog,
636
+ {{external}}
643
637
  {{output}}
644
- },
645
- },
646
- test: {
647
- name: { label: 'src:browser', color: 'yellow' },
648
- include: ['tests/src/browser/**/*.test.ts'],
649
- {{exclude}} setupFiles: ['./tests/setup.ts', './tests/setupBrowser.ts'],
638
+ },
639
+ },
640
+ test: {
641
+ name: { label: 'src:browser', color: 'yellow' },
642
+ include: ['tests/src/browser/**/*.test.ts'],
643
+ {{exclude}} setupFiles: ['./tests/setup.ts', './tests/setupBrowser.ts'],
650
644
  {{global}}
651
- browser: {
652
- enabled: true,
653
- provider: playwright(browserOptions),
654
- instances: [{ browser: 'chromium', headless: true }],
655
- },
656
- fileParallelism: false,
657
- },
645
+ browser: {
646
+ enabled: true,
647
+ provider: playwright(browserOptions),
648
+ instances: [{ browser: 'chromium', headless: true }],
658
649
  },
659
- options ?? {},
660
- )
650
+ fileParallelism: false,
651
+ },
652
+ })
661
653
  `,
662
- server: `export const srcServer = (options?: UserConfig): UserConfig =>
663
- mergeConfig(
664
- {
665
- resolve,
666
- publicDir: false,
667
- plugins: [outputBoundary('dist/src/server'), environmentBoundary('src/server')],
668
- build: {
669
- emptyOutDir: true,
670
- sourcemap: true,
671
- minify: false,
672
- lib: {
673
- entry: resolveWorkspacePath('src/server/index.ts'),
674
- formats: ['es', 'cjs'],
675
- fileName: (format: string) => (format === 'es' ? 'index.js' : 'index.cjs'),
676
- },
677
- outDir: 'dist/src/server',
678
- target: 'node22',
679
- rolldownOptions: {
680
- onLog: enforceBuildLog,
681
- platform: 'node',
682
- {{external}}
654
+ server: `export const srcServer = (): UserConfig => ({
655
+ resolve,
656
+ publicDir: false,
657
+ plugins: [outputBoundary('dist/src/server'), environmentBoundary('src/server')],
658
+ build: {
659
+ emptyOutDir: true,
660
+ sourcemap: true,
661
+ minify: false,
662
+ lib: {
663
+ entry: resolveWorkspacePath('src/server/index.ts'),
664
+ formats: ['es', 'cjs'],
665
+ fileName: (format: string) => (format === 'es' ? 'index.js' : 'index.cjs'),
666
+ },
667
+ outDir: 'dist/src/server',
668
+ target: 'node22',
669
+ rolldownOptions: {
670
+ onLog: enforceBuildLog,
671
+ platform: 'node',
672
+ {{external}}
683
673
  {{output}}
684
- },
685
- },
686
- test: {
687
- name: { label: 'src:server', color: 'red' },
688
- include: ['tests/src/server/**/*.test.ts'],
689
- {{exclude}} setupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],
690
- environment: 'node',
691
- browser: { enabled: false },
692
- },
693
674
  },
694
- options ?? {},
695
- )
675
+ },
676
+ test: {
677
+ name: { label: 'src:server', color: 'red' },
678
+ include: ['tests/src/server/**/*.test.ts'],
679
+ {{exclude}} setupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],
680
+ environment: 'node',
681
+ browser: { enabled: false },
682
+ },
683
+ })
696
684
  `,
697
- bin: `export const srcBin = (options?: UserConfig): UserConfig =>
698
- mergeConfig(
699
- {
700
- resolve,
701
- publicDir: false,
702
- plugins: [outputBoundary('dist/bin')],
703
- build: {
704
- emptyOutDir: true,
705
- sourcemap: true,
706
- minify: false,
707
- lib: {
708
- entry: resolveWorkspacePath('${BIN_ENTRY_PATH}'),
709
- formats: ['es'],
710
- fileName: () => 'main.js',
711
- },
712
- outDir: 'dist/bin',
713
- target: 'node22',
714
- rolldownOptions: {
715
- onLog: enforceBuildLog,
716
- external: (id: string) =>
717
- id.startsWith('node:') ||
718
- id.startsWith('@orkestrel/') ||
719
- id.startsWith('@src/') ||
720
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),
721
- },
722
- },
723
- test: {
724
- name: { label: 'src:bin', color: 'yellow' },
725
- include: ['tests/src/bin/**/*.test.ts'],
726
- setupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],
727
- environment: 'node',
728
- browser: { enabled: false },
729
- // A bin test drives the real executable over a real temporary repository, so it
730
- // spends seconds in process startup and filesystem work rather than milliseconds.
731
- // Vitest's five-second default clears one alone and times out under a full suite.
732
- testTimeout: 15_000,
733
- },
685
+ bin: `export const srcBin = (): UserConfig => ({
686
+ resolve,
687
+ publicDir: false,
688
+ plugins: [outputBoundary('dist/bin')],
689
+ build: {
690
+ emptyOutDir: true,
691
+ sourcemap: true,
692
+ minify: false,
693
+ lib: {
694
+ entry: resolveWorkspacePath('${BIN_ENTRY_PATH}'),
695
+ formats: ['es'],
696
+ fileName: () => 'main.js',
734
697
  },
735
- options ?? {},
736
- )
698
+ outDir: 'dist/bin',
699
+ target: 'node22',
700
+ rolldownOptions: {
701
+ onLog: enforceBuildLog,
702
+ external: (id: string) =>
703
+ id.startsWith('node:') ||
704
+ id.startsWith('@orkestrel/') ||
705
+ id.startsWith('@src/') ||
706
+ peers.some((peer) => id === peer || id.startsWith(peer + '/')),
707
+ },
708
+ },
709
+ test: {
710
+ name: { label: 'src:bin', color: 'yellow' },
711
+ include: ['tests/src/bin/**/*.test.ts'],
712
+ setupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],
713
+ environment: 'node',
714
+ browser: { enabled: false },
715
+ // A bin test drives the real executable over a real temporary repository, so it
716
+ // spends seconds in process startup and filesystem work rather than milliseconds.
717
+ // Vitest's five-second default clears one alone and times out under a full suite.
718
+ testTimeout: 15_000,
719
+ },
720
+ })
737
721
  `
738
722
  }),
739
723
  app: Object.freeze({
740
- core: `export const appCore = (options?: UserConfig): UserConfig =>
741
- mergeConfig(
742
- {
743
- resolve,
744
- publicDir: false,
745
- plugins: [environmentBoundary('app/core')],
746
- test: {
747
- name: { label: 'app:core', color: 'cyan' },
748
- include: ['tests/app/core/**/*.test.ts'],
749
- setupFiles: ['./tests/setup.ts'],
750
- environment: 'node',
751
- browser: { enabled: false },
752
- },
753
- },
754
- options ?? {},
755
- )
724
+ core: `export const appCore = (): UserConfig => ({
725
+ resolve,
726
+ publicDir: false,
727
+ plugins: [environmentBoundary('app/core')],
728
+ test: {
729
+ name: { label: 'app:core', color: 'cyan' },
730
+ include: ['tests/app/core/**/*.test.ts'],
731
+ setupFiles: ['./tests/setup.ts'],
732
+ environment: 'node',
733
+ browser: { enabled: false },
734
+ },
735
+ })
756
736
  `,
757
737
  browser: `function applicationBrowser(showcase: boolean): UserConfig {
758
738
  const output = showcase ? 'dist/showcase' : 'dist/app/browser'
@@ -784,197 +764,157 @@ const resolve = {
784
764
  }
785
765
  }
786
766
 
787
- export function appBrowser(options?: UserConfig): UserConfig {
788
- return mergeConfig(applicationBrowser(false), options ?? {})
767
+ export function appBrowser(): UserConfig {
768
+ return applicationBrowser(false)
789
769
  }
790
770
  {{showcaseFactory}}`,
791
- server: `export const appServer = (options?: UserConfig): UserConfig =>
792
- mergeConfig(
793
- {
794
- resolve,
795
- publicDir: false,
796
- plugins: [outputBoundary('dist/app/server'), environmentBoundary('app/server')],
797
- build: {
798
- emptyOutDir: true,
799
- lib: {
800
- entry: resolveWorkspacePath('app/server/main.ts'),
801
- formats: ['cjs'],
802
- fileName: () => 'main.cjs',
803
- },
804
- outDir: resolveWorkspacePath('dist/app/server'),
805
- target: 'node22',
806
- rolldownOptions: {
807
- onLog: enforceBuildLog,
808
- external: (id: string) => id.startsWith('node:'),
809
- },
810
- },
811
- test: {
812
- name: { label: 'app:server', color: 'green' },
813
- include: ['tests/app/server/**/*.test.ts'],
814
- setupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],
815
- environment: 'node',
816
- browser: { enabled: false },
817
- },
771
+ server: `export const appServer = (): UserConfig => ({
772
+ resolve,
773
+ publicDir: false,
774
+ plugins: [outputBoundary('dist/app/server'), environmentBoundary('app/server')],
775
+ build: {
776
+ emptyOutDir: true,
777
+ lib: {
778
+ entry: resolveWorkspacePath('app/server/main.ts'),
779
+ formats: ['cjs'],
780
+ fileName: () => 'main.cjs',
818
781
  },
819
- options ?? {},
820
- )
782
+ outDir: resolveWorkspacePath('dist/app/server'),
783
+ target: 'node22',
784
+ rolldownOptions: {
785
+ onLog: enforceBuildLog,
786
+ external: (id: string) => id.startsWith('node:'),
787
+ },
788
+ },
789
+ test: {
790
+ name: { label: 'app:server', color: 'green' },
791
+ include: ['tests/app/server/**/*.test.ts'],
792
+ setupFiles: ['./tests/setup.ts', './tests/setupServer.ts'],
793
+ environment: 'node',
794
+ browser: { enabled: false },
795
+ },
796
+ })
821
797
  `
822
798
  }),
823
- policy: `export const policy = (options?: UserConfig): UserConfig =>
824
- mergeConfig(
825
- {
826
- resolve,
827
- test: {
828
- name: { label: 'policy', color: 'white' },
829
- include: ['tests/policy.test.ts'],
830
- setupFiles: ['./tests/setup.ts'],
831
- environment: 'node',
832
- browser: { enabled: false },
833
- },
834
- },
835
- options ?? {},
836
- )
799
+ policy: `export const policy = (): UserConfig => ({
800
+ resolve,
801
+ test: {
802
+ name: { label: 'policy', color: 'white' },
803
+ include: ['tests/policy.test.ts'],
804
+ setupFiles: ['./tests/setup.ts'],
805
+ environment: 'node',
806
+ browser: { enabled: false },
807
+ },
808
+ })
837
809
  `,
838
- config: `export const config = (options?: UserConfig): UserConfig =>
839
- mergeConfig(
840
- {
841
- resolve,
842
- test: {
843
- name: { label: 'config', color: 'yellow' },
844
- include: ['tests/config.test.ts'],
845
- setupFiles: ['./tests/setup.ts'],
846
- environment: 'node',
847
- browser: { enabled: false },
848
- // A config test validates every target wrapper and runs the real linter twice with
849
- // 15-second child caps, so this budget clears both caps and reports their diagnostics.
850
- testTimeout: 45_000,
851
- },
852
- },
853
- options ?? {},
854
- )
810
+ config: `export const config = (): UserConfig => ({
811
+ resolve,
812
+ test: {
813
+ name: { label: 'config', color: 'yellow' },
814
+ include: ['tests/config.test.ts'],
815
+ setupFiles: ['./tests/setup.ts'],
816
+ environment: 'node',
817
+ browser: { enabled: false },
818
+ // A config test validates every target wrapper and runs the real linter twice with
819
+ // 15-second child caps, so this budget clears both caps and reports their diagnostics.
820
+ testTimeout: 45_000,
821
+ },
822
+ })
855
823
  `,
856
- setup: `export const setup = (options?: UserConfig): UserConfig =>
857
- mergeConfig(
858
- {
859
- resolve,
860
- test: {
861
- name: { label: 'setup', color: 'white' },
862
- include: ['tests/setup*.test.ts'],
863
- setupFiles: ['./tests/setup.ts'],
864
- environment: 'node',
865
- browser: { enabled: false },
866
- },
867
- },
868
- options ?? {},
869
- )
824
+ setup: `export const setup = (): UserConfig => ({
825
+ resolve,
826
+ test: {
827
+ name: { label: 'setup', color: 'white' },
828
+ include: ['tests/setup*.test.ts'],
829
+ setupFiles: ['./tests/setup.ts'],
830
+ environment: 'node',
831
+ browser: { enabled: false },
832
+ },
833
+ })
870
834
  `,
871
- guides: `export const guides = (options?: UserConfig): UserConfig =>
872
- mergeConfig(
873
- {
874
- resolve,
875
- test: {
876
- name: { label: 'guides', color: 'green' },
877
- include: ['${GUIDES_TEST_PATH}'],
878
- exclude: ['tests/src/**/*.test.ts', 'tests/app/**/*.test.ts', 'tests/setup.test.ts'],
879
- setupFiles: ['./tests/setup.ts'],
880
- environment: 'node',
881
- browser: { enabled: false },
882
- },
883
- },
884
- options ?? {},
885
- )
835
+ guides: `export const guides = (): UserConfig => ({
836
+ resolve,
837
+ test: {
838
+ name: { label: 'guides', color: 'green' },
839
+ include: ['${GUIDES_TEST_PATH}'],
840
+ exclude: ['tests/src/**/*.test.ts', 'tests/app/**/*.test.ts', 'tests/setup.test.ts'],
841
+ setupFiles: ['./tests/setup.ts'],
842
+ environment: 'node',
843
+ browser: { enabled: false },
844
+ },
845
+ })
886
846
  `,
887
847
  conformance: `// Where this package drifts from the official tooling it stays compatible with.
888
848
  // The subject is this package, so the proof is hermetic and stays in \`npm test\`.
889
- export const conformance = (options?: UserConfig): UserConfig =>
890
- mergeConfig(
891
- {
892
- resolve,
893
- test: {
894
- name: { label: 'conformance', color: 'magenta' },
895
- include: ['${CONFORMANCE_TEST_PATH}'],
896
- setupFiles: ['./tests/setup.ts'],
897
- environment: 'node',
898
- browser: { enabled: false },
899
- },
900
- },
901
- options ?? {},
902
- )
849
+ export const conformance = (): UserConfig => ({
850
+ resolve,
851
+ test: {
852
+ name: { label: 'conformance', color: 'magenta' },
853
+ include: ['${CONFORMANCE_TEST_PATH}'],
854
+ setupFiles: ['./tests/setup.ts'],
855
+ environment: 'node',
856
+ browser: { enabled: false },
857
+ },
858
+ })
903
859
  `,
904
860
  service: `// The live external services this package drives. It starts nothing itself:
905
861
  // \`scripts/service.sh\` provisions, \`tests/setupService.ts\` proves readiness, and
906
862
  // the project stays out of \`npm test\` because a real service answers it.
907
- export const service = (options?: UserConfig): UserConfig =>
908
- mergeConfig(
909
- {
910
- resolve,
911
- test: {
912
- name: { label: 'service', color: 'red' },
913
- include: ['${SERVICE_TEST_INCLUDE}'],
914
- setupFiles: ['./tests/setup.ts', './tests/setupService.ts'],
915
- environment: 'node',
916
- browser: { enabled: false },
917
- testTimeout: 120_000,
918
- hookTimeout: 120_000,
919
- fileParallelism: false,
920
- },
921
- },
922
- options ?? {},
923
- )
863
+ export const service = (): UserConfig => ({
864
+ resolve,
865
+ test: {
866
+ name: { label: 'service', color: 'red' },
867
+ include: ['${SERVICE_TEST_INCLUDE}'],
868
+ setupFiles: ['./tests/setup.ts', './tests/setupService.ts'],
869
+ environment: 'node',
870
+ browser: { enabled: false },
871
+ testTimeout: 120_000,
872
+ hookTimeout: 120_000,
873
+ fileParallelism: false,
874
+ },
875
+ })
924
876
  `,
925
- distribution: `export const distribution = (options?: UserConfig): UserConfig =>
926
- mergeConfig(
927
- {
928
- resolve,
929
- test: {
930
- name: { label: 'distribution', color: 'cyan' },
931
- include: ['${DISTRIBUTION_TEST_PATH}'],
932
- setupFiles: ['./tests/setup.ts'],
933
- environment: 'node',
934
- testTimeout: 120_000,
935
- hookTimeout: 120_000,
936
- fileParallelism: false,
937
- },
938
- },
939
- options ?? {},
940
- )
877
+ distribution: `export const distribution = (): UserConfig => ({
878
+ resolve,
879
+ test: {
880
+ name: { label: 'distribution', color: 'cyan' },
881
+ include: ['${DISTRIBUTION_TEST_PATH}'],
882
+ setupFiles: ['./tests/setup.ts'],
883
+ environment: 'node',
884
+ testTimeout: 120_000,
885
+ hookTimeout: 120_000,
886
+ fileParallelism: false,
887
+ },
888
+ })
941
889
  `,
942
890
  probe: `// A workbench, not a proof. No gate selects this project. Run in test mode by the
943
891
  // \`test:probe\` script, it collects \`tmp/probe/**/*.test.ts\`. Run in benchmark mode by the
944
892
  // \`test:bench\` script, the same workbench also collects \`tests/**/*.test.ts\` for a \`bench\` block,
945
893
  // so a suite may carry a bench beside its ordinary tests without a second project. The mode
946
894
  // guard around each \`bench\` call keeps it out of test mode, so it never executes there.
947
- export const probe = (options?: UserConfig): UserConfig =>
948
- mergeConfig(
949
- {
950
- resolve,
951
- test: {
952
- name: { label: 'probe', color: 'gray' },
953
- include: ['tmp/probe/**/*.test.ts'],
954
- setupFiles: ['./tests/setup.ts'],
955
- environment: 'node',
956
- browser: { enabled: false },
957
- fileParallelism: false,
958
- pool: 'threads',
959
- benchmark: { include: ['tmp/probe/**/*.test.ts', 'tests/**/*.test.ts'] },
960
- },
961
- },
962
- options ?? {},
963
- )
895
+ export const probe = (): UserConfig => ({
896
+ resolve,
897
+ test: {
898
+ name: { label: 'probe', color: 'black' },
899
+ include: ['tmp/probe/**/*.test.ts'],
900
+ setupFiles: ['./tests/setup.ts'],
901
+ environment: 'node',
902
+ browser: { enabled: false },
903
+ fileParallelism: false,
904
+ pool: 'threads',
905
+ benchmark: { include: ['tmp/probe/**/*.test.ts', 'tests/**/*.test.ts'] },
906
+ },
907
+ })
964
908
  `,
965
- integration: `export const integration = (options?: UserConfig): UserConfig =>
966
- mergeConfig(
967
- {
968
- resolve,
969
- test: {
970
- name: { label: 'integration', color: 'blue' },
971
- include: ['${INTEGRATION_TEST_PATH}'],
972
- setupFiles: ['./tests/setup.ts'],
973
- {{global}} environment: 'node',
974
- },
975
- },
976
- options ?? {},
977
- )
909
+ integration: `export const integration = (): UserConfig => ({
910
+ resolve,
911
+ test: {
912
+ name: { label: 'integration', color: 'blue' },
913
+ include: ['${INTEGRATION_TEST_PATH}'],
914
+ setupFiles: ['./tests/setup.ts'],
915
+ {{global}} environment: 'node',
916
+ },
917
+ })
978
918
  `
979
919
  }),
980
920
  tsconfigs: Object.freeze({
@@ -1102,13 +1042,13 @@ export const probe = (options?: UserConfig): UserConfig =>
1102
1042
  }),
1103
1043
  vites: Object.freeze({
1104
1044
  src: Object.freeze({
1105
- core: `import { defineConfig } from 'vite'
1045
+ core: `import { defineConfig, mergeConfig } from 'vite'
1106
1046
  import dts from 'vite-plugin-dts'
1107
1047
  import { environmentBoundary, outputBoundary } from '../helpers.js'
1108
1048
  import { peers, srcCore, resolveWorkspacePath } from '../../vite.config.ts'
1109
1049
 
1110
1050
  export default defineConfig(
1111
- srcCore({
1051
+ mergeConfig(srcCore(), {
1112
1052
  publicDir: false,
1113
1053
  plugins: [
1114
1054
  outputBoundary('dist/src/core'),
@@ -1130,7 +1070,7 @@ export default defineConfig(
1130
1070
  lib: {
1131
1071
  entry: resolveWorkspacePath('src/core/index.ts'),
1132
1072
  formats: ['es', 'cjs'],
1133
- fileName: (format) => (format === 'es' ? 'index.js' : 'index.cjs'),
1073
+ fileName: (format: string) => (format === 'es' ? 'index.js' : 'index.cjs'),
1134
1074
  },
1135
1075
  outDir: 'dist/src/core',
1136
1076
  rolldownOptions: {
@@ -1143,17 +1083,17 @@ export default defineConfig(
1143
1083
  }),
1144
1084
  )
1145
1085
  `,
1146
- browser: `import { defineConfig } from 'vite'
1086
+ browser: `import { defineConfig, mergeConfig } from 'vite'
1147
1087
  import dts from 'vite-plugin-dts'
1148
1088
  import { srcBrowser, resolveWorkspacePath } from '../../vite.config.ts'
1149
1089
 
1150
1090
  // vite-plugin-dts rolls this face into one declaration, and the roll-up reaches
1151
1091
  // src/core through a relative source path the tarball does not carry. The path
1152
1092
  // keeps each source module's own depth, so a module in a browser subfolder emits
1153
- // one that leaves dist/src entirely. The rewrite below externalizes core through
1154
- // the package's own published root export, on the final roll-up only.
1093
+ // one that leaves dist/src entirely. The following rewrite externalizes core
1094
+ // through the package's own published root export, on the final roll-up only.
1155
1095
  export default defineConfig(
1156
- srcBrowser({
1096
+ mergeConfig(srcBrowser(), {
1157
1097
  plugins: [
1158
1098
  dts({
1159
1099
  tsconfigPath: resolveWorkspacePath('configs/src/tsconfig.browser.json'),
@@ -1168,16 +1108,16 @@ export default defineConfig(
1168
1108
  }),
1169
1109
  )
1170
1110
  `,
1171
- server: `import { defineConfig } from 'vite'
1111
+ server: `import { defineConfig, mergeConfig } from 'vite'
1172
1112
  import dts from 'vite-plugin-dts'
1173
1113
  import { srcServer, resolveWorkspacePath } from '../../vite.config.ts'
1174
1114
 
1175
1115
  // vite-plugin-dts rolls this face into one declaration, and the roll-up reaches
1176
- // src/core through a relative source path the tarball does not carry. The rewrite
1177
- // below externalizes core through the package's own published root export, on the
1178
- // final roll-up only.
1116
+ // src/core through a relative source path the tarball does not carry. The
1117
+ // following rewrite externalizes core through the package's own published root
1118
+ // export, on the final roll-up only.
1179
1119
  export default defineConfig(
1180
- srcServer({
1120
+ mergeConfig(srcServer(), {
1181
1121
  plugins: [
1182
1122
  dts({
1183
1123
  tsconfigPath: resolveWorkspacePath('configs/src/tsconfig.server.json'),
@@ -1193,7 +1133,7 @@ export default defineConfig(
1193
1133
  )
1194
1134
  `
1195
1135
  }),
1196
- bin: `import { defineConfig } from 'vite'
1136
+ bin: `import { defineConfig, mergeConfig } from 'vite'
1197
1137
  import { srcBin } from '../../vite.config.ts'
1198
1138
 
1199
1139
  // The \`scaffold\` executable build — a single ESM lib file, no declarations (an
@@ -1202,7 +1142,7 @@ import { srcBin } from '../../vite.config.ts'
1202
1142
  // \`output.paths\` rewriting the externalized \`@src/*\` specifiers to the built sibling
1203
1143
  // src environments (relative to \`dist/bin/\`), so the emitted bin resolves at runtime.
1204
1144
  export default defineConfig(
1205
- srcBin({
1145
+ mergeConfig(srcBin(), {
1206
1146
  build: {
1207
1147
  rolldownOptions: {
1208
1148
  output: {
@@ -1640,8 +1580,8 @@ const SHELL = process.platform === 'win32'
1640
1580
  // Release is the publish gate, so evidence it cannot obtain fails there and skips
1641
1581
  // everywhere else: a gate that passes on missing evidence proves nothing.
1642
1582
  const RELEASE = import.meta.env.MODE === 'release'
1643
- // The built output directory a browser face is published from. Every selection here
1644
- // reads this prefix off the export TARGET and never off the subpath name. A
1583
+ // The built output directory convention a browser face may publish from. Every
1584
+ // selection reads this prefix off the export target and never off the subpath name. A
1645
1585
  // workspace whose only published face is the browser one publishes that face at the
1646
1586
  // root subpath, so a rule keyed on the subpath name drives a browser bundle through
1647
1587
  // Node and the miss is silent.
@@ -1929,11 +1869,33 @@ function readPackageType(installed: string, target: string): unknown {
1929
1869
  }
1930
1870
  }
1931
1871
 
1932
- // Whether the declaration selected by a typed CommonJS consumer admits that entry
1933
- // to its compile probe. A \`.d.cts\` declaration admits and a \`.d.mts\`
1934
- // declaration refuses. A \`.d.ts\` declaration takes its own nearest package scope.
1935
- // Runtime target format remains the runtime drive's separate question.
1872
+ function resolvesBrowser(entry: unknown): boolean {
1873
+ const module = resolveTarget(entry, RUNTIME_CONDITIONS.browser)
1874
+ if (module !== undefined && module.startsWith(BROWSER_OUTPUT)) return true
1875
+ if (module === undefined) return false
1876
+ const imported = resolveTarget(entry, RUNTIME_CONDITIONS.module)
1877
+ const required = resolveTarget(entry, RUNTIME_CONDITIONS.commonjs)
1878
+ return module !== imported && module !== required
1879
+ }
1880
+
1881
+ // Whether the target selected by Node's CommonJS conditions is a module require can
1882
+ // load. A JavaScript target takes its own nearest package scope. Native addons and
1883
+ // extensionless targets have their own CommonJS handlers.
1936
1884
  function resolvesCommonJS(entry: unknown, installed: string): boolean {
1885
+ const target = resolveTarget(entry, RUNTIME_CONDITIONS.commonjs)
1886
+ if (target === undefined) return false
1887
+ const name = target.slice(target.lastIndexOf('/') + 1)
1888
+ if (name.endsWith('.cjs')) return true
1889
+ if (name.endsWith('.mjs')) return false
1890
+ if (name.endsWith('.node')) return true
1891
+ if (!name.includes('.')) return true
1892
+ return name.endsWith('.js') && readPackageType(installed, target) !== 'module'
1893
+ }
1894
+
1895
+ // Whether the declaration selected by a typed CommonJS consumer admits that entry.
1896
+ // A \`.d.cts\` declaration admits and a \`.d.mts\` declaration refuses. A \`.d.ts\`
1897
+ // declaration takes its own nearest package scope.
1898
+ function declaresCommonJS(entry: unknown, installed: string): boolean {
1937
1899
  const declaration = resolveDeclaration(entry, DECLARATION_CONDITIONS.commonjs, installed)
1938
1900
  if (declaration === undefined) return false
1939
1901
  if (declaration.endsWith('.d.cts')) return true
@@ -1996,13 +1958,13 @@ function selectEntries(entries: readonly Entry[], conditions: readonly string[])
1996
1958
  // Require-loadable entries that declare CommonJS support but a typed CommonJS
1997
1959
  // consumer cannot compile against. A default branch resolving under the require
1998
1960
  // condition set makes no CommonJS claim.
1999
- function selectUntypable(entries: readonly Entry[]): readonly Entry[] {
1961
+ function selectUntypable(entries: readonly Entry[], installed: string): readonly Entry[] {
2000
1962
  return entries.filter(
2001
1963
  (entry) =>
2002
1964
  entry.required &&
2003
1965
  isRecord(entry.mapping) &&
2004
1966
  Object.hasOwn(entry.mapping, 'require') &&
2005
- !entry.commonjs,
1967
+ !declaresCommonJS(entry.mapping, installed),
2006
1968
  )
2007
1969
  }
2008
1970
 
@@ -2135,9 +2097,11 @@ function buildStage(): Stage {
2135
2097
  continue
2136
2098
  }
2137
2099
  const imported = resolveTarget(entry, RUNTIME_CONDITIONS.module)
2138
- const commonjs = resolvesCommonJS(entry, installed)
2139
- const required = resolveTarget(entry, RUNTIME_CONDITIONS.commonjs) !== undefined
2140
- const module = resolveTarget(entry, RUNTIME_CONDITIONS.browser)
2100
+ const requiredTarget = resolveTarget(entry, RUNTIME_CONDITIONS.commonjs)
2101
+ const browserTarget = resolveTarget(entry, RUNTIME_CONDITIONS.browser)
2102
+ const browser = resolvesBrowser(entry)
2103
+ const required = requiredTarget !== undefined && !(browser && requiredTarget === browserTarget)
2104
+ const commonjs = required && resolvesCommonJS(entry, installed)
2141
2105
  entries.push({
2142
2106
  subpath,
2143
2107
  specifier: subpath === '.' ? name : \`\${name}\${subpath.slice(1)}\`,
@@ -2149,8 +2113,8 @@ function buildStage(): Stage {
2149
2113
  browser:
2150
2114
  declaration.browser === undefined ? undefined : join(installed, declaration.browser),
2151
2115
  },
2152
- browser: module !== undefined && module.startsWith(BROWSER_OUTPUT),
2153
- module: imported !== undefined,
2116
+ browser,
2117
+ module: imported !== undefined && !(browser && imported === browserTarget),
2154
2118
  commonjs,
2155
2119
  required,
2156
2120
  })
@@ -2191,6 +2155,74 @@ function openStage(): Stage | undefined {
2191
2155
  const STAGE = openStage()
2192
2156
  const STAGED = STAGE !== undefined
2193
2157
 
2158
+ describe('distribution classifiers', () => {
2159
+ it('classifies synthetic export mappings without a registry stage', () => {
2160
+ const root = join(SCRATCH, 'classifiers')
2161
+ writeFile(
2162
+ join(root, 'package.json'),
2163
+ JSON.stringify({
2164
+ type: 'commonjs',
2165
+ exports: {
2166
+ condition: { browser: './b.js', default: './n.js' },
2167
+ convention: { default: './dist/src/browser/index.js' },
2168
+ universal: { default: './shared.js' },
2169
+ 'import-shared': {
2170
+ browser: './shared.mjs',
2171
+ import: './shared.mjs',
2172
+ default: './node.js',
2173
+ },
2174
+ 'require-shared': {
2175
+ browser: './shared.cjs',
2176
+ require: './shared.cjs',
2177
+ default: './node.js',
2178
+ },
2179
+ node: { node: './node.js', default: './node.js' },
2180
+ silent: { 'module-sync': './x.cjs', import: './x.mjs' },
2181
+ module: { require: './x.mjs' },
2182
+ 'nested-module': { require: './module/x.js' },
2183
+ 'nested-commonjs': { require: './commonjs/x.js' },
2184
+ esm: { import: './x.mjs' },
2185
+ },
2186
+ }),
2187
+ )
2188
+ writeFile(join(root, 'module/package.json'), '{ "type": "module" }\\n')
2189
+ writeFile(join(root, 'commonjs/package.json'), '{ "type": "commonjs" }\\n')
2190
+ const manifest = readJson(join(root, 'package.json'))
2191
+ if (!isRecord(manifest) || !isRecord(manifest.exports)) {
2192
+ throw new Error('The classifier fixture declares no exports map')
2193
+ }
2194
+ const mappings = manifest.exports
2195
+ expect({
2196
+ condition: resolvesBrowser(mappings.condition),
2197
+ convention: resolvesBrowser(mappings.convention),
2198
+ universal: resolvesBrowser(mappings.universal),
2199
+ import: resolvesBrowser(mappings['import-shared']),
2200
+ require: resolvesBrowser(mappings['require-shared']),
2201
+ node: resolvesBrowser(mappings.node),
2202
+ }).toStrictEqual({
2203
+ condition: true,
2204
+ convention: true,
2205
+ universal: false,
2206
+ import: false,
2207
+ require: false,
2208
+ node: false,
2209
+ })
2210
+ expect({
2211
+ silent: resolvesCommonJS(mappings.silent, root),
2212
+ module: resolvesCommonJS(mappings.module, root),
2213
+ nestedModule: resolvesCommonJS(mappings['nested-module'], root),
2214
+ nestedCommonJS: resolvesCommonJS(mappings['nested-commonjs'], root),
2215
+ esm: resolvesCommonJS(mappings.esm, root),
2216
+ }).toStrictEqual({
2217
+ silent: true,
2218
+ module: false,
2219
+ nestedModule: false,
2220
+ nestedCommonJS: true,
2221
+ esm: false,
2222
+ })
2223
+ })
2224
+ })
2225
+
2194
2226
  // The staged consumer, or a skip naming what the run could not reach. \`it.skipIf\`
2195
2227
  // carries no reason, so the gate sits here where the test context can state one.
2196
2228
  function requireStage(context: TestContext): Stage {
@@ -2238,7 +2270,7 @@ describe('installed package consumer', () => {
2238
2270
  (entry) => !entry.module && !entry.required && !entry.browser,
2239
2271
  )
2240
2272
  expect(unreachable.map((entry) => entry.subpath)).toStrictEqual([])
2241
- const untypable = selectUntypable(stage.entries)
2273
+ const untypable = selectUntypable(stage.entries, stage.installed)
2242
2274
  expect(untypable.map((entry) => entry.subpath)).toStrictEqual([])
2243
2275
  })
2244
2276
 
@@ -2283,7 +2315,7 @@ describe('installed package consumer', () => {
2283
2315
 
2284
2316
  for (const entry of STAGE?.entries ?? []) {
2285
2317
  describe(\`installed entry \${entry.subpath}\`, () => {
2286
- it.runIf(entry.module && !entry.browser)(
2318
+ it.runIf(entry.module)(
2287
2319
  'publishes what it declares to a Node import, and no more',
2288
2320
  (context) => {
2289
2321
  const declaration = entry.declaration.module
@@ -2295,7 +2327,7 @@ for (const entry of STAGE?.entries ?? []) {
2295
2327
  },
2296
2328
  )
2297
2329
 
2298
- it.runIf(!entry.browser && entry.required)(
2330
+ it.runIf(entry.required)(
2299
2331
  'publishes what it declares to a Node require, and no more',
2300
2332
  (context) => {
2301
2333
  const declaration = entry.declaration.commonjs
@@ -4317,7 +4349,7 @@ function blueprintToScripts(blueprint) {
4317
4349
  scripts["serve:build"] = "npm run build:app:server && npm run serve";
4318
4350
  }
4319
4351
  if (publishes) {
4320
- scripts.prepack = scripts.build;
4352
+ scripts.prepack = "npm run build";
4321
4353
  scripts.prepublishOnly = [
4322
4354
  "npm run format:check && npm run lint:check && npm run check && npm run build && npm test",
4323
4355
  RELEASE_PROOF_COMMAND,
@@ -4330,19 +4362,21 @@ function blueprintToScripts(blueprint) {
4330
4362
  * Project a blueprint into the manifest scripts a region write may replace.
4331
4363
  *
4332
4364
  * @param blueprint - The workspace specification.
4333
- * @returns One entry per writable script, or none when the workspace publishes
4334
- * nothing and therefore declares neither script.
4365
+ * @returns One entry per writable script.
4335
4366
  *
4336
4367
  * @remarks
4337
- * Publishing is what selects these scripts, so a private workspace answers an
4338
- * empty region and the write leaves its manifest alone.
4339
- *
4340
- * `accepted` carries the predecessor a target can hold before this package
4341
- * generated the packed-package proof: the same gate chain without
4368
+ * Every direct `test:<project>` script is writable, together with the probe and
4369
+ * benchmark workbench scripts. Publishing adds the pack and publication
4370
+ * lifecycle scripts. Aggregate test scripts and maintainer-owned gate chains
4371
+ * stay outside the region.
4372
+ *
4373
+ * `accepted` carries each generated predecessor the region can replace. The
4374
+ * pack hook accepts the build chain emitted before it delegated to `build`. The
4375
+ * publication hook accepts the same gate chain without
4342
4376
  * {@link RELEASE_PROOF_COMMAND}. The value being written is always writable, so
4343
- * it is not repeated there. Any other value is a chain the workspace author
4344
- * customized, and {@link replaceManifestScripts} refuses the whole region
4345
- * rather than taking it.
4377
+ * it is not repeated there. Any other value is a script the workspace author
4378
+ * customized, and {@link replaceManifestScripts} retains it while writing the
4379
+ * other named scripts independently.
4346
4380
  *
4347
4381
  * @example
4348
4382
  * ```ts
@@ -4350,23 +4384,36 @@ function blueprintToScripts(blueprint) {
4350
4384
  *
4351
4385
  * const blueprint = createBlueprint('router', { src: ['core'] })
4352
4386
  *
4353
- * blueprintToWritableScripts(blueprint)[0]?.name // 'test:distribution'
4387
+ * blueprintToWritableScripts(blueprint)[0]?.name // 'test:src:core'
4354
4388
  * ```
4355
4389
  */
4356
4390
  function blueprintToWritableScripts(blueprint) {
4357
4391
  const scripts = blueprintToScripts(blueprint);
4358
- const distribution = scripts["test:distribution"];
4392
+ const writable = [];
4393
+ for (const [name, command] of Object.entries(scripts)) {
4394
+ if (!name.startsWith("test:") || name === "test:src" || name === "test:app") continue;
4395
+ writable.push({
4396
+ name,
4397
+ command,
4398
+ accepted: []
4399
+ });
4400
+ }
4401
+ const prepack = scripts.prepack;
4402
+ if (prepack !== void 0) {
4403
+ const predecessor = scripts.build;
4404
+ writable.push({
4405
+ name: "prepack",
4406
+ command: prepack,
4407
+ accepted: predecessor === void 0 ? [] : [predecessor]
4408
+ });
4409
+ }
4359
4410
  const prepublish = scripts.prepublishOnly;
4360
- if (distribution === void 0 || prepublish === void 0) return [];
4361
- return [{
4362
- name: "test:distribution",
4363
- command: distribution,
4364
- accepted: []
4365
- }, {
4411
+ if (prepublish !== void 0) writable.push({
4366
4412
  name: "prepublishOnly",
4367
4413
  command: prepublish,
4368
4414
  accepted: [prepublish.replace(` && ${RELEASE_PROOF_COMMAND}`, "")]
4369
- }];
4415
+ });
4416
+ return writable;
4370
4417
  }
4371
4418
  /**
4372
4419
  * Compile a blueprint into its `package.json` content.
@@ -4549,14 +4596,14 @@ function blueprintToRootVite(blueprint) {
4549
4596
  const core = blueprint.src.includes("core");
4550
4597
  factories.push(fillTemplate(CONFIG_TEMPLATES.factories.src.browser, {
4551
4598
  external: core ? `external: (id: string) =>
4552
- id === '@src/core' ||
4553
- id.startsWith('@orkestrel/') ||
4554
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),` : `external: (id: string) =>
4555
- id.startsWith('@orkestrel/') ||
4556
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),`,
4557
- output: core ? " output: { paths: { '@src/core': '../core/index.js' } }," : " output: {},",
4558
- exclude: core ? " exclude: ['tests/src/core/**/*.test.ts'],\n" : "",
4559
- global: blueprint.global ? " globalSetup: ['./tests/setupGlobal.ts'],\n" : ""
4599
+ id === '@src/core' ||
4600
+ id.startsWith('@orkestrel/') ||
4601
+ peers.some((peer) => id === peer || id.startsWith(peer + '/')),` : `external: (id: string) =>
4602
+ id.startsWith('@orkestrel/') ||
4603
+ peers.some((peer) => id === peer || id.startsWith(peer + '/')),`,
4604
+ output: core ? " output: { paths: { '@src/core': '../core/index.js' } }," : " output: {},",
4605
+ exclude: core ? " exclude: ['tests/src/core/**/*.test.ts'],\n" : "",
4606
+ global: blueprint.global ? " globalSetup: ['./tests/setupGlobal.ts'],\n" : ""
4560
4607
  }));
4561
4608
  projects.push("srcBrowser");
4562
4609
  }
@@ -4564,26 +4611,26 @@ function blueprintToRootVite(blueprint) {
4564
4611
  const core = blueprint.src.includes("core");
4565
4612
  factories.push(fillTemplate(CONFIG_TEMPLATES.factories.src.server, {
4566
4613
  external: core ? `external: (id: string) =>
4567
- id === '@src/core' ||
4568
- id.startsWith('node:') ||
4569
- id.startsWith('@orkestrel/') ||
4570
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),` : `external: (id: string) =>
4571
- id.startsWith('node:') ||
4572
- id.startsWith('@orkestrel/') ||
4573
- peers.some((peer) => id === peer || id.startsWith(peer + '/')),`,
4574
- output: core ? `\t\t\t\t\toutput: [
4575
- {
4576
- format: 'es',
4577
- entryFileNames: 'index.js',
4578
- paths: { '@src/core': '../core/index.js' },
4579
- },
4580
- {
4581
- format: 'cjs',
4582
- entryFileNames: 'index.cjs',
4583
- paths: { '@src/core': '../core/index.cjs' },
4584
- },
4585
- ],` : " output: {},",
4586
- exclude: core ? " exclude: ['tests/src/core/**/*.test.ts'],\n" : ""
4614
+ id === '@src/core' ||
4615
+ id.startsWith('node:') ||
4616
+ id.startsWith('@orkestrel/') ||
4617
+ peers.some((peer) => id === peer || id.startsWith(peer + '/')),` : `external: (id: string) =>
4618
+ id.startsWith('node:') ||
4619
+ id.startsWith('@orkestrel/') ||
4620
+ peers.some((peer) => id === peer || id.startsWith(peer + '/')),`,
4621
+ output: core ? `\t\t\toutput: [
4622
+ {
4623
+ format: 'es',
4624
+ entryFileNames: 'index.js',
4625
+ paths: { '@src/core': '../core/index.js' },
4626
+ },
4627
+ {
4628
+ format: 'cjs',
4629
+ entryFileNames: 'index.cjs',
4630
+ paths: { '@src/core': '../core/index.cjs' },
4631
+ },
4632
+ ],` : " output: {},",
4633
+ exclude: core ? " exclude: ['tests/src/core/**/*.test.ts'],\n" : ""
4587
4634
  }));
4588
4635
  projects.push("srcServer");
4589
4636
  }
@@ -4678,7 +4725,7 @@ export function appShowcase(): UserConfig {
4678
4725
  projects.push("distribution");
4679
4726
  }
4680
4727
  if (blueprint.integration) {
4681
- factories.push(fillTemplate(CONFIG_TEMPLATES.factories.integration, { global: blueprint.global ? " globalSetup: ['./tests/setupGlobal.ts'],\n" : "" }));
4728
+ factories.push(fillTemplate(CONFIG_TEMPLATES.factories.integration, { global: blueprint.global ? " globalSetup: ['./tests/setupGlobal.ts'],\n" : "" }));
4682
4729
  projects.push("integration");
4683
4730
  }
4684
4731
  factories.push(CONFIG_TEMPLATES.factories.probe);
@@ -5381,24 +5428,23 @@ function replaceManifestRanges(manifest, pins) {
5381
5428
  *
5382
5429
  * @param manifest - The manifest text to compile.
5383
5430
  * @param scripts - The scripts to write, each with the predecessors it accepts.
5384
- * @returns The manifest carrying every named script, or `undefined` when a
5385
- * named script holds a value outside what it accepts, or when the text carries
5386
- * no readable `scripts` object to write into.
5431
+ * @returns The manifest carrying every absent or accepted named script and
5432
+ * retaining every differing string value, or `undefined` when a planned key
5433
+ * holds a non-string value or the text carries no readable `scripts` object to
5434
+ * write into.
5387
5435
  *
5388
5436
  * @remarks
5389
5437
  * The compiler replaces values in place instead of serializing the manifest, so
5390
5438
  * description, keywords, dependencies, key order, indentation, and every byte
5391
5439
  * outside the replaced ranges survive. A named script the manifest already
5392
- * declares is overwritten only when its value is the one being written or one
5393
- * of its {@link ManifestScript.accepted} predecessors; anything else is a chain
5394
- * the workspace author customized, and the whole region is refused without a
5395
- * byte moving. A named script the manifest does not declare is appended after
5396
- * the last declared script, copying that section's indentation. A region
5397
- * declaring nothing takes every named script as its first entries, indented
5398
- * from the line its own opening brace sits on.
5399
- *
5400
- * The refusal is whole rather than per script, so a manifest never ends up
5401
- * holding one written script beside one refused one.
5440
+ * declares is overwritten only when its value is one of its
5441
+ * {@link ManifestScript.accepted} predecessors. The planned value stands. Any
5442
+ * other string is a chain the workspace author customized, so it stays
5443
+ * byte-identical while the other named scripts are written independently. A
5444
+ * named script the manifest does not declare is appended after the last
5445
+ * declared script, copying that section's indentation. A region declaring
5446
+ * nothing takes every named script as its first entries, indented from the line
5447
+ * its own opening brace sits on.
5402
5448
  *
5403
5449
  * @example
5404
5450
  * ```ts
@@ -5420,7 +5466,6 @@ function replaceManifestScripts(manifest, scripts) {
5420
5466
  if (!Object.hasOwn(declared, script.name)) continue;
5421
5467
  const value = declared[script.name];
5422
5468
  if (!isString(value)) return void 0;
5423
- if (value !== script.command && !script.accepted.includes(value)) return void 0;
5424
5469
  }
5425
5470
  let depth = 0;
5426
5471
  let cursor = 0;
@@ -5548,6 +5593,8 @@ function replaceManifestScripts(manifest, scripts) {
5548
5593
  const script = scripts.find((entry) => entry.name === key);
5549
5594
  if (script === void 0) continue;
5550
5595
  written.add(script.name);
5596
+ const value = declared[script.name];
5597
+ if (value === script.command || !isString(value) || !script.accepted.includes(value)) continue;
5551
5598
  edits.push({
5552
5599
  start: valueStart,
5553
5600
  end: valueEnd + 1,