3131 * `integration` (behaviour)"), and ⛔ nothing is renamed to make the two cuts
3232 * agree.
3333 *
34+ * ## ⚠️ WHICH NAME REACHES THIS CHECK CHANGED (#16726)
35+ *
36+ * A charset gate now sits in FRONT of this one: a name outside the charset
37+ * `packages/spec` declares for an object `name` is refused before anything is
38+ * derived from it, so it never reaches the compiler at all. `foo.bar` — the
39+ * card's measured name — is one of those, and it still exits 1 having written
40+ * nothing, which is the defect #16541 was filed about. What it no longer
41+ * demonstrates is THIS check: the refusal it now meets is the gate's.
42+ *
43+ * So the parse check is measured through `class`, added here for that purpose.
44+ * It is inside the charset (every character is a lowercase letter), the gate
45+ * admits it, and `const class:` is still not a declaration — so it is the name
46+ * that proves this command consults the compiler before it writes, and that
47+ * the layer in front did not swallow the layer behind. ⛔ Nothing was deleted
48+ * to make room for it: every `foo.bar` assertion that is still about the
49+ * COMMAND (exit code, no rewrite, nothing on disk) is asserted below unchanged.
50+ *
51+ * One property genuinely stopped being reachable from here: a name that breaks
52+ * the BARREL line as well as the scaffold. A reserved word is legal as an
53+ * `export { default as … }` alias, so no charset-legal name breaks both, and
54+ * every name that does is now stopped one layer earlier. That half stays
55+ * pinned where it still runs — `generate-emission-parses.test.ts`'s CANARY row
56+ * measures `foo.bar` against both emissions, with the instrument this command
57+ * calls.
58+ *
3459 * ## The control is load-bearing
3560 *
3661 * A refusal that fires on everything would satisfy every assertion about
@@ -115,28 +140,34 @@ function parseErrors(source: string): string[] {
115140let refusedDir : string ;
116141let controlDir : string ;
117142let dryRunDir : string ;
143+ let unparseableDir : string ;
118144
119145let refused : Run ;
120146let refusedAgain : Run ;
121147let control : Run ;
122148let dryRun : Run ;
149+ let unparseable : Run ;
123150
124151beforeAll ( async ( ) => {
125152 refusedDir = mkdtempSync ( join ( tmpdir ( ) , 'os-g-refuse-' ) ) ;
126153 controlDir = mkdtempSync ( join ( tmpdir ( ) , 'os-g-control-' ) ) ;
127154 dryRunDir = mkdtempSync ( join ( tmpdir ( ) , 'os-g-dryrun-' ) ) ;
155+ unparseableDir = mkdtempSync ( join ( tmpdir ( ) , 'os-g-unparseable-' ) ) ;
128156
129157 // Sequential on purpose: cold tsx starts, each loading every command module,
130158 // in a container several agents share.
131159 refused = await runTsx ( [ CLI , 'generate' , 'object' , 'foo.bar' ] , refusedDir ) ;
132160 // A second generator, to show the refusal is not one patched call site.
133161 refusedAgain = await runTsx ( [ CLI , 'generate' , 'flow' , 'foo.bar' ] , refusedDir ) ;
134162 dryRun = await runTsx ( [ CLI , 'generate' , 'object' , 'foo.bar' , '--dry-run' ] , dryRunDir ) ;
163+ // Inside the charset, outside the grammar — the name that reaches THIS
164+ // check now that a charset gate stands in front of it (#16726).
165+ unparseable = await runTsx ( [ CLI , 'generate' , 'object' , 'class' ] , unparseableDir ) ;
135166 control = await runTsx ( [ CLI , 'generate' , 'object' , 'order_line' ] , controlDir ) ;
136167} , RUN_TIMEOUT_MS ) ;
137168
138169afterAll ( ( ) => {
139- for ( const dir of [ refusedDir , controlDir , dryRunDir ] ) {
170+ for ( const dir of [ refusedDir , controlDir , dryRunDir , unparseableDir ] ) {
140171 rmSync ( dir , { recursive : true , force : true } ) ;
141172 }
142173} ) ;
@@ -147,21 +178,9 @@ describe('[#16541] `os generate object foo.bar` refuses instead of exiting 0', (
147178 expect ( refused . code ) . toBe ( 1 ) ;
148179 } ) ;
149180
150- it ( 'says it is refusing, and says the emission does not parse ' , ( ) => {
181+ it ( 'says it is refusing, and names the value it refused ' , ( ) => {
151182 expect ( refused . stdout ) . toContain ( 'Refusing to generate' ) ;
152- expect ( refused . stdout ) . toContain ( 'does not parse' ) ;
153- } ) ;
154-
155- it ( 'names BOTH files the name would have corrupted' , ( ) => {
156- expect ( refused . stdout ) . toContain ( 'foo.bar.object.ts' ) ;
157- expect ( refused . stdout ) . toContain ( 'index.ts' ) ;
158- } ) ;
159-
160- it ( 'quotes the compiler`s own diagnostic rather than a restatement of it' , ( ) => {
161- // The message TypeScript emits for a property access in a binding
162- // position. Asserted because a hand-written "invalid name" line would pass
163- // every other assertion in this block.
164- expect ( refused . stdout ) . toContain ( "',' expected." ) ;
183+ expect ( refused . stdout ) . toContain ( 'foo.bar' ) ;
165184 } ) ;
166185
167186 it ( '⛔ does not rewrite the name into a legal-looking identifier' , ( ) => {
@@ -178,6 +197,33 @@ describe('[#16541] `os generate object foo.bar` refuses instead of exiting 0', (
178197 } ) ;
179198} ) ;
180199
200+ describe ( '[#16541] the parse check still refuses, with the compiler`s own words' , ( ) => {
201+ // `class`, not `foo.bar` — see the header. Everything asserted here is what
202+ // #16541 asserted about `foo.bar` before the #16726 gate started answering
203+ // for that spelling first.
204+ it ( 'exits non-zero and says the emission does not parse' , ( ) => {
205+ expect ( unparseable . code ) . toBe ( 1 ) ;
206+ expect ( unparseable . stdout ) . toContain ( 'Refusing to generate' ) ;
207+ expect ( unparseable . stdout ) . toContain ( 'does not parse' ) ;
208+ } ) ;
209+
210+ it ( 'names the file the name would have corrupted' , ( ) => {
211+ expect ( unparseable . stdout ) . toContain ( 'class.object.ts' ) ;
212+ } ) ;
213+
214+ it ( 'quotes the compiler`s own diagnostic rather than a restatement of it' , ( ) => {
215+ // The message TypeScript emits for a reserved word in a binding position.
216+ // Asserted because a hand-written "invalid name" line would pass every
217+ // other assertion in this block — and because it proves the gate in front
218+ // did not answer for this name.
219+ expect ( unparseable . stdout ) . toContain ( "'class' is not allowed as a variable declaration name." ) ;
220+ } ) ;
221+
222+ it ( 'writes nothing' , ( ) => {
223+ expect ( existsSync ( join ( unparseableDir , 'src' ) ) ) . toBe ( false ) ;
224+ } ) ;
225+ } ) ;
226+
181227describe ( '[#16541] the refusal is one chokepoint, not one patched generator' , ( ) => {
182228 it ( '`os generate flow foo.bar` is refused the same way' , ( ) => {
183229 expect ( refusedAgain . code ) . toBe ( 1 ) ;
0 commit comments