@orkestrel/scaffold 0.0.30 → 0.0.32
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.
- package/dist/bin/main.js +144 -23
- package/dist/bin/main.js.map +1 -1
- package/dist/host/agents/orchestration.md +48 -6
- package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +12 -0
- package/dist/host/claude/agents/codex.md +10 -0
- package/dist/host/claude/agents/grok.md +19 -8
- package/dist/host/claude/rules/architecture.md +25 -0
- package/dist/host/claude/rules/documentation.md +2 -1
- package/dist/host/claude/rules/tests.md +58 -21
- package/dist/host/claude/rules/workspace.md +25 -20
- package/dist/host/dotfiles/oxlintrc.json +1 -0
- package/dist/host/guides/scaffold.md +142 -67
- package/dist/host/tests/config.test.ts +45 -17
- package/dist/host/tests/policy.test.ts +254 -2
- package/dist/host/tests/setupPolicy.ts +297 -34
- package/dist/src/core/index.cjs +176 -179
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +90 -33
- package/dist/src/core/index.d.ts +90 -33
- package/dist/src/core/index.js +171 -180
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +13 -1
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.js +14 -2
- package/dist/src/server/index.js.map +1 -1
- package/package.json +6 -5
|
@@ -95,9 +95,22 @@ export const DATA_SOURCE_FILES: readonly string[] = Object.freeze([
|
|
|
95
95
|
'validators.ts',
|
|
96
96
|
])
|
|
97
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Files excluded from the module-data rule because their namespace values hold helper behavior.
|
|
100
|
+
* This exclusion also permits unrelated module data such as `export const RETRIES = 3`.
|
|
101
|
+
*/
|
|
102
|
+
export const DATA_EXEMPT_FILES: readonly string[] = Object.freeze(['helpers.ts'])
|
|
103
|
+
|
|
98
104
|
/** Fleet-registered folders whose direct modules each contain one named function. */
|
|
99
105
|
export const FUNCTION_DOMAIN_FOLDERS: readonly string[] = Object.freeze(['app/browser/composables'])
|
|
100
106
|
|
|
107
|
+
/** Every ambient declaration suffix the source glob collects and the parsed population excludes. */
|
|
108
|
+
export const POLICY_AMBIENT_SUFFIXES: readonly string[] = Object.freeze([
|
|
109
|
+
'.d.cts',
|
|
110
|
+
'.d.mts',
|
|
111
|
+
'.d.ts',
|
|
112
|
+
])
|
|
113
|
+
|
|
101
114
|
/** TypeScript source extensions whose declaration syntax the sweep reads. */
|
|
102
115
|
export const POLICY_SOURCE_EXTENSIONS: readonly string[] = Object.freeze([
|
|
103
116
|
'cts',
|
|
@@ -106,9 +119,32 @@ export const POLICY_SOURCE_EXTENSIONS: readonly string[] = Object.freeze([
|
|
|
106
119
|
'tsx',
|
|
107
120
|
])
|
|
108
121
|
|
|
109
|
-
/**
|
|
122
|
+
/** Every extension through which a mirrored test can name a module. */
|
|
123
|
+
export const POLICY_MODULE_EXTENSIONS: readonly string[] = Object.freeze([
|
|
124
|
+
'cts',
|
|
125
|
+
'mts',
|
|
126
|
+
'ts',
|
|
127
|
+
'tsx',
|
|
128
|
+
'vue',
|
|
129
|
+
'scss',
|
|
130
|
+
'css',
|
|
131
|
+
])
|
|
132
|
+
|
|
133
|
+
/** Module extensions whose extensionless stem can resolve a leading-underscore partial. */
|
|
134
|
+
export const POLICY_PARTIAL_EXTENSIONS: readonly string[] = Object.freeze(['scss', 'css'])
|
|
135
|
+
|
|
136
|
+
/** The reserved stem prefix whose mirrored module can resolve inside the tests axis. */
|
|
137
|
+
export const POLICY_TESTS_MODULE_PREFIX = 'setup'
|
|
138
|
+
|
|
139
|
+
/** The complete parsed TypeScript source population under either workspace axis. */
|
|
110
140
|
export const POLICY_SOURCE_GLOB = `{app,src}/**/*.{${POLICY_SOURCE_EXTENSIONS.join(',')}}`
|
|
111
141
|
|
|
142
|
+
/** The complete module population available to mirrored tests under either workspace axis. */
|
|
143
|
+
export const POLICY_MODULE_GLOB = `{app,src}/**/*.{${POLICY_MODULE_EXTENSIONS.join(',')}}`
|
|
144
|
+
|
|
145
|
+
/** The tests-axis setup module population available to mirrored tests. */
|
|
146
|
+
export const POLICY_TESTS_MODULE_GLOB = `tests/**/${POLICY_TESTS_MODULE_PREFIX}*.ts`
|
|
147
|
+
|
|
112
148
|
/** The mirrored module-test population inspected under either workspace axis. */
|
|
113
149
|
export const POLICY_TEST_GLOB = 'tests/{app,src}/**/*.test.ts'
|
|
114
150
|
|
|
@@ -165,6 +201,23 @@ export function isFunctionDomainPath(path: string): boolean {
|
|
|
165
201
|
)
|
|
166
202
|
}
|
|
167
203
|
|
|
204
|
+
/**
|
|
205
|
+
* Read the function expression an expression holds directly, through any parentheses.
|
|
206
|
+
*
|
|
207
|
+
* @param expression - The expression to unwrap.
|
|
208
|
+
* @returns The arrow or function expression it holds directly, or `undefined` for anything else.
|
|
209
|
+
*/
|
|
210
|
+
export function expressionToPolicyFunction(
|
|
211
|
+
expression: ts.Expression | undefined,
|
|
212
|
+
): ts.ArrowFunction | ts.FunctionExpression | undefined {
|
|
213
|
+
let current = expression
|
|
214
|
+
while (current !== undefined && ts.isParenthesizedExpression(current)) {
|
|
215
|
+
current = current.expression
|
|
216
|
+
}
|
|
217
|
+
if (current === undefined) return undefined
|
|
218
|
+
return ts.isArrowFunction(current) || ts.isFunctionExpression(current) ? current : undefined
|
|
219
|
+
}
|
|
220
|
+
|
|
168
221
|
/**
|
|
169
222
|
* Whether a variable initializer is directly a function expression.
|
|
170
223
|
*
|
|
@@ -172,29 +225,91 @@ export function isFunctionDomainPath(path: string): boolean {
|
|
|
172
225
|
* @returns `true` for a direct arrow or function expression.
|
|
173
226
|
*/
|
|
174
227
|
export function isPolicyFunctionInitializer(initializer: ts.Expression | undefined): boolean {
|
|
175
|
-
|
|
176
|
-
while (expression !== undefined && ts.isParenthesizedExpression(expression)) {
|
|
177
|
-
expression = expression.expression
|
|
178
|
-
}
|
|
179
|
-
return (
|
|
180
|
-
expression !== undefined &&
|
|
181
|
-
(ts.isArrowFunction(expression) || ts.isFunctionExpression(expression))
|
|
182
|
-
)
|
|
228
|
+
return expressionToPolicyFunction(initializer) !== undefined
|
|
183
229
|
}
|
|
184
230
|
|
|
185
231
|
/**
|
|
186
|
-
* Whether
|
|
232
|
+
* Whether a module region holds a module policy function.
|
|
233
|
+
*
|
|
234
|
+
* A module region is syntax outside every permitted function. A permitted function's parameters and
|
|
235
|
+
* the arguments of a call sitting inside one are read under this same rule, because the law grants
|
|
236
|
+
* those positions nothing extra.
|
|
187
237
|
*
|
|
188
|
-
* @param node - The
|
|
189
|
-
* @returns `true` when the
|
|
238
|
+
* @param node - The module region to inspect.
|
|
239
|
+
* @returns `true` when the region holds an arrow or function expression the law does not permit.
|
|
240
|
+
* An arrow or function expression passed directly as a call or `new` argument is exempt itself, and
|
|
241
|
+
* reports only for what its parameters and body nest. A class expression is never function syntax.
|
|
190
242
|
*/
|
|
191
|
-
export function
|
|
243
|
+
export function hasModulePolicyFunction(node: ts.Node | undefined): boolean {
|
|
192
244
|
if (node === undefined) return false
|
|
193
245
|
if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) return true
|
|
194
246
|
if (ts.isClassExpression(node)) return false
|
|
247
|
+
if (ts.isCallExpression(node) || ts.isNewExpression(node)) {
|
|
248
|
+
if (hasModulePolicyFunction(node.expression)) return true
|
|
249
|
+
if (node.arguments !== undefined) {
|
|
250
|
+
for (const argument of node.arguments) {
|
|
251
|
+
const permitted = expressionToPolicyFunction(argument)
|
|
252
|
+
if (permitted === undefined) {
|
|
253
|
+
if (hasModulePolicyFunction(argument)) return true
|
|
254
|
+
} else if (nestsPolicyFunction(permitted)) return true
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return false
|
|
258
|
+
}
|
|
195
259
|
let found = false
|
|
196
260
|
ts.forEachChild(node, (child) => {
|
|
197
|
-
if (!found &&
|
|
261
|
+
if (!found && hasModulePolicyFunction(child)) found = true
|
|
262
|
+
})
|
|
263
|
+
return found
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Whether a permitted function nests a policy function the law does not permit.
|
|
268
|
+
*
|
|
269
|
+
* The function is exempt in its own right. Its parameters read as a module region, and its body
|
|
270
|
+
* reads as the region inside a permitted function, where a nested function keeps permission only by
|
|
271
|
+
* qualifying independently as a direct callback or result.
|
|
272
|
+
*
|
|
273
|
+
* @param node - The permitted callback or returned function to inspect.
|
|
274
|
+
* @returns `true` when its parameters or body nest function syntax the law does not permit.
|
|
275
|
+
*/
|
|
276
|
+
export function nestsPolicyFunction(node: ts.ArrowFunction | ts.FunctionExpression): boolean {
|
|
277
|
+
for (const parameter of node.parameters) {
|
|
278
|
+
if (hasModulePolicyFunction(parameter)) return true
|
|
279
|
+
}
|
|
280
|
+
if (!ts.isBlock(node.body)) {
|
|
281
|
+
const returned = expressionToPolicyFunction(node.body)
|
|
282
|
+
if (returned !== undefined) return nestsPolicyFunction(returned)
|
|
283
|
+
}
|
|
284
|
+
return hasNestedPolicyFunction(node.body)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Whether a region inside a permitted function holds a nested policy function.
|
|
289
|
+
*
|
|
290
|
+
* A direct return keeps its permission through control flow. Every other nested function reports
|
|
291
|
+
* unless it independently qualifies as a direct callback. Class-expression members stay outside
|
|
292
|
+
* the instrument's reach.
|
|
293
|
+
*
|
|
294
|
+
* @param node - The region inside a permitted function to inspect.
|
|
295
|
+
* @returns `true` when the region holds function syntax the law does not permit.
|
|
296
|
+
*/
|
|
297
|
+
export function hasNestedPolicyFunction(node: ts.Node): boolean {
|
|
298
|
+
if (ts.isFunctionDeclaration(node)) return true
|
|
299
|
+
if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) return true
|
|
300
|
+
if (ts.isClassExpression(node)) return false
|
|
301
|
+
if (ts.isReturnStatement(node)) {
|
|
302
|
+
const returned = expressionToPolicyFunction(node.expression)
|
|
303
|
+
return returned === undefined
|
|
304
|
+
? hasModulePolicyFunction(node.expression)
|
|
305
|
+
: nestsPolicyFunction(returned)
|
|
306
|
+
}
|
|
307
|
+
if (ts.isCallExpression(node) || ts.isNewExpression(node)) {
|
|
308
|
+
return hasModulePolicyFunction(node)
|
|
309
|
+
}
|
|
310
|
+
let found = false
|
|
311
|
+
ts.forEachChild(node, (child) => {
|
|
312
|
+
if (!found && hasNestedPolicyFunction(child)) found = true
|
|
198
313
|
})
|
|
199
314
|
return found
|
|
200
315
|
}
|
|
@@ -302,8 +417,8 @@ export function inspectPolicyVariables(
|
|
|
302
417
|
const violations: PolicyViolation[] = []
|
|
303
418
|
for (const declaration of statement.declarationList.declarations) {
|
|
304
419
|
const directFunction = isPolicyFunctionInitializer(declaration.initializer)
|
|
305
|
-
const containsFunction =
|
|
306
|
-
if (!directFunction && !DATA_SOURCE_FILES.includes(file)) {
|
|
420
|
+
const containsFunction = hasModulePolicyFunction(declaration.initializer)
|
|
421
|
+
if (!directFunction && !DATA_SOURCE_FILES.includes(file) && !DATA_EXEMPT_FILES.includes(file)) {
|
|
307
422
|
violations.push(
|
|
308
423
|
createPolicyViolation('data', path, 'module data sits in a data-kind file', declaration),
|
|
309
424
|
)
|
|
@@ -488,7 +603,7 @@ export function inspectPolicySource(source: PolicySource): readonly PolicyViolat
|
|
|
488
603
|
}
|
|
489
604
|
|
|
490
605
|
/**
|
|
491
|
-
* Inspect an explicit
|
|
606
|
+
* Inspect an explicit parsed population through the same per-file route as the workspace sweep.
|
|
492
607
|
*
|
|
493
608
|
* @param sources - The TypeScript files to inspect.
|
|
494
609
|
* @returns Every syntactic placement violation in source order.
|
|
@@ -500,51 +615,77 @@ export function inspectPolicySources(sources: readonly PolicySource[]): readonly
|
|
|
500
615
|
}
|
|
501
616
|
|
|
502
617
|
/**
|
|
503
|
-
* Read the
|
|
618
|
+
* Read the parsed TypeScript source population beneath one workspace.
|
|
504
619
|
*
|
|
505
620
|
* @param root - The workspace root to read.
|
|
506
|
-
* @returns Every TypeScript source under the src and app axes, sorted by path.
|
|
621
|
+
* @returns Every non-ambient TypeScript source under the src and app axes, sorted by path.
|
|
507
622
|
*/
|
|
508
623
|
export function readPolicySources(root: string): readonly PolicySource[] {
|
|
509
624
|
return globSync(POLICY_SOURCE_GLOB, { cwd: root })
|
|
625
|
+
.map(normalizePolicyPath)
|
|
626
|
+
.filter((path) => !POLICY_AMBIENT_SUFFIXES.some((suffix) => basename(path).endsWith(suffix)))
|
|
510
627
|
.sort()
|
|
511
628
|
.map((path) => ({
|
|
512
|
-
path
|
|
629
|
+
path,
|
|
513
630
|
content: readFileSync(join(root, path), 'utf8'),
|
|
514
631
|
}))
|
|
515
632
|
}
|
|
516
633
|
|
|
517
634
|
/**
|
|
518
|
-
* Derive the
|
|
635
|
+
* Derive the extensionless module stem for one mirrored module test.
|
|
519
636
|
*
|
|
520
637
|
* @param path - The workspace-relative test path.
|
|
521
|
-
* @returns The
|
|
638
|
+
* @returns The extensionless module stem, or `undefined` for a reserved scope test.
|
|
522
639
|
*/
|
|
523
|
-
export function
|
|
640
|
+
export function testToPolicyStem(path: string): string | undefined {
|
|
524
641
|
const normalized = normalizePolicyPath(path)
|
|
525
642
|
if (basename(normalized) === 'integration.test.ts') return undefined
|
|
526
643
|
if (!normalized.startsWith('tests/') || !normalized.endsWith('.test.ts')) return undefined
|
|
527
|
-
return
|
|
644
|
+
return normalized.slice('tests/'.length, -'.test.ts'.length)
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* The candidate set is every module name a registered language resolves for a stem.
|
|
649
|
+
*
|
|
650
|
+
* @param stem - The extensionless workspace-relative module stem.
|
|
651
|
+
* @returns Direct modules, partial modules, then a matching tests-axis setup module.
|
|
652
|
+
*/
|
|
653
|
+
export function stemToPolicyCandidates(stem: string): readonly string[] {
|
|
654
|
+
const normalized = normalizePolicyPath(stem)
|
|
655
|
+
const directory = dirname(normalized).replaceAll('\\', '/')
|
|
656
|
+
const name = basename(normalized)
|
|
657
|
+
const candidates = POLICY_MODULE_EXTENSIONS.map((extension) => `${normalized}.${extension}`)
|
|
658
|
+
for (const extension of POLICY_PARTIAL_EXTENSIONS) {
|
|
659
|
+
candidates.push(`${directory}/_${name}.${extension}`)
|
|
660
|
+
}
|
|
661
|
+
if (name.startsWith(POLICY_TESTS_MODULE_PREFIX)) candidates.push(`tests/${normalized}.ts`)
|
|
662
|
+
return candidates
|
|
528
663
|
}
|
|
529
664
|
|
|
530
665
|
/**
|
|
531
|
-
* Inspect mirrored test paths against an explicit
|
|
666
|
+
* Inspect mirrored test paths against an explicit module-path population.
|
|
532
667
|
*
|
|
533
668
|
* @param tests - The module-test paths to inspect.
|
|
534
|
-
* @param
|
|
669
|
+
* @param modules - The existing module paths in every registered language.
|
|
535
670
|
* @returns Every missing mirror violation in test-path order.
|
|
536
671
|
*/
|
|
537
672
|
export function inspectPolicyMirrorPaths(
|
|
538
673
|
tests: readonly string[],
|
|
539
|
-
|
|
674
|
+
modules: ReadonlySet<string>,
|
|
540
675
|
): readonly PolicyViolation[] {
|
|
541
676
|
const violations: PolicyViolation[] = []
|
|
542
677
|
for (const test of tests) {
|
|
543
678
|
const path = normalizePolicyPath(test)
|
|
544
|
-
const
|
|
545
|
-
if (
|
|
679
|
+
const stem = testToPolicyStem(path)
|
|
680
|
+
if (stem === undefined) continue
|
|
681
|
+
const candidates = stemToPolicyCandidates(stem)
|
|
682
|
+
if (!candidates.some((candidate) => modules.has(candidate))) {
|
|
546
683
|
violations.push(
|
|
547
|
-
createPolicyViolation(
|
|
684
|
+
createPolicyViolation(
|
|
685
|
+
'mirror',
|
|
686
|
+
path,
|
|
687
|
+
`module test requires one matching module: ${candidates.join(', ')}`,
|
|
688
|
+
),
|
|
548
689
|
)
|
|
549
690
|
}
|
|
550
691
|
}
|
|
@@ -559,10 +700,11 @@ export function inspectPolicyMirrorPaths(
|
|
|
559
700
|
*/
|
|
560
701
|
export function inspectPolicyMirrors(root: string): readonly PolicyViolation[] {
|
|
561
702
|
const tests = globSync(POLICY_TEST_GLOB, { cwd: root }).sort().map(normalizePolicyPath)
|
|
562
|
-
const
|
|
563
|
-
globSync(
|
|
564
|
-
|
|
565
|
-
|
|
703
|
+
const modules = new Set([
|
|
704
|
+
...globSync(POLICY_MODULE_GLOB, { cwd: root }).sort().map(normalizePolicyPath),
|
|
705
|
+
...globSync(POLICY_TESTS_MODULE_GLOB, { cwd: root }).sort().map(normalizePolicyPath),
|
|
706
|
+
])
|
|
707
|
+
return inspectPolicyMirrorPaths(tests, modules)
|
|
566
708
|
}
|
|
567
709
|
|
|
568
710
|
/**
|
|
@@ -698,6 +840,127 @@ export const POLICY_CONTROLS: readonly PolicyControl[] = Object.freeze([
|
|
|
698
840
|
},
|
|
699
841
|
],
|
|
700
842
|
},
|
|
843
|
+
{
|
|
844
|
+
label: 'rejects a module whose stem only prefixes the test stem',
|
|
845
|
+
membership: 'module paths whose exact extensionless stem differs from the test stem',
|
|
846
|
+
rule: 'mirror',
|
|
847
|
+
files: [
|
|
848
|
+
{ path: 'app/browser/WidgetPanel.vue', content: '<template></template>\n' },
|
|
849
|
+
{ path: 'tests/app/browser/Widget.test.ts', content: '' },
|
|
850
|
+
],
|
|
851
|
+
},
|
|
852
|
+
{
|
|
853
|
+
label: 'rejects a partial whose stem differs from the test stem',
|
|
854
|
+
membership: 'partial module paths whose exact underscore-free stem differs from the test stem',
|
|
855
|
+
rule: 'mirror',
|
|
856
|
+
files: [
|
|
857
|
+
{ path: 'app/browser/styles/_token.scss', content: '' },
|
|
858
|
+
{ path: 'tests/app/browser/styles/tokens.test.ts', content: '' },
|
|
859
|
+
],
|
|
860
|
+
},
|
|
861
|
+
{
|
|
862
|
+
label: 'rejects a test with no module candidate',
|
|
863
|
+
membership: 'module tests with no matching module path in any registered form',
|
|
864
|
+
rule: 'mirror',
|
|
865
|
+
files: [{ path: 'tests/app/browser/Widget.test.ts', content: '' }],
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
label: 'rejects a non-setup module inside tests',
|
|
869
|
+
membership: 'tests-axis modules whose stem does not start with setup',
|
|
870
|
+
rule: 'mirror',
|
|
871
|
+
files: [
|
|
872
|
+
{ path: 'tests/app/core/widget.ts', content: '' },
|
|
873
|
+
{ path: 'tests/app/core/widget.test.ts', content: '' },
|
|
874
|
+
],
|
|
875
|
+
},
|
|
876
|
+
{
|
|
877
|
+
label: 'rejects a type in a non-ambient env module',
|
|
878
|
+
membership: 'non-ambient TypeScript modules whose filename is not types.ts',
|
|
879
|
+
rule: 'type',
|
|
880
|
+
files: [{ path: 'app/browser/env.ts', content: 'export interface EnvironmentInterface {}\n' }],
|
|
881
|
+
},
|
|
882
|
+
{
|
|
883
|
+
label: 'rejects a property-held arrow in constants.ts',
|
|
884
|
+
membership: 'module-level function syntax not passed directly as an argument',
|
|
885
|
+
rule: 'function',
|
|
886
|
+
files: [
|
|
887
|
+
{
|
|
888
|
+
path: 'app/edge/constants.ts',
|
|
889
|
+
content: 'export const HANDLERS = Object.freeze({ run: () => undefined })\n',
|
|
890
|
+
},
|
|
891
|
+
],
|
|
892
|
+
},
|
|
893
|
+
{
|
|
894
|
+
label: 'rejects a callback parameter default function',
|
|
895
|
+
membership: 'function expressions in callback parameter defaults',
|
|
896
|
+
rule: 'function',
|
|
897
|
+
files: [
|
|
898
|
+
{
|
|
899
|
+
path: 'app/edge/constants.ts',
|
|
900
|
+
content: 'export const VALUES = Object.freeze(C.map((c = () => 1) => c))\n',
|
|
901
|
+
},
|
|
902
|
+
],
|
|
903
|
+
},
|
|
904
|
+
{
|
|
905
|
+
label: 'rejects a destructured callback parameter default function',
|
|
906
|
+
membership: 'function expressions in destructured callback parameter defaults',
|
|
907
|
+
rule: 'function',
|
|
908
|
+
files: [
|
|
909
|
+
{
|
|
910
|
+
path: 'app/edge/constants.ts',
|
|
911
|
+
content: 'export const VALUES = Object.freeze(C.map(({ f = () => 1 }) => f))\n',
|
|
912
|
+
},
|
|
913
|
+
],
|
|
914
|
+
},
|
|
915
|
+
{
|
|
916
|
+
label: 'rejects an assignment inside callback control flow',
|
|
917
|
+
membership: 'function assignments inside callback control-flow branches',
|
|
918
|
+
rule: 'function',
|
|
919
|
+
files: [
|
|
920
|
+
{
|
|
921
|
+
path: 'app/edge/constants.ts',
|
|
922
|
+
content:
|
|
923
|
+
'export const VALUES = Object.freeze(C.map((c) => { if (c) { const f = () => 1; return f() } return 2 }))\n',
|
|
924
|
+
},
|
|
925
|
+
],
|
|
926
|
+
},
|
|
927
|
+
{
|
|
928
|
+
label: 'rejects an assignment inside a direct callback',
|
|
929
|
+
membership: 'function assignments inside the body of a callback passed directly as an argument',
|
|
930
|
+
rule: 'function',
|
|
931
|
+
files: [
|
|
932
|
+
{
|
|
933
|
+
path: 'app/edge/constants.ts',
|
|
934
|
+
content:
|
|
935
|
+
'export const VALUES = Object.freeze(C.map((c) => { const f = () => c; return f() }))\n',
|
|
936
|
+
},
|
|
937
|
+
],
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
label: 'rejects a declaration inside a direct callback',
|
|
941
|
+
membership:
|
|
942
|
+
'function declarations inside the body of a callback passed directly as an argument',
|
|
943
|
+
rule: 'function',
|
|
944
|
+
files: [
|
|
945
|
+
{
|
|
946
|
+
path: 'app/edge/constants.ts',
|
|
947
|
+
content:
|
|
948
|
+
'export const LABELS = Object.freeze(COLUMNS.map((column) => { function format() { return column.label } return format() }))\n',
|
|
949
|
+
},
|
|
950
|
+
],
|
|
951
|
+
},
|
|
952
|
+
{
|
|
953
|
+
label: 'rejects an assignment two direct callbacks down',
|
|
954
|
+
membership: 'function assignments inside a callback the outer callback passes directly',
|
|
955
|
+
rule: 'function',
|
|
956
|
+
files: [
|
|
957
|
+
{
|
|
958
|
+
path: 'app/edge/constants.ts',
|
|
959
|
+
content:
|
|
960
|
+
'export const VALUES = Object.freeze(C.map((c) => wrap((d) => { const g = () => d; return g() })))\n',
|
|
961
|
+
},
|
|
962
|
+
],
|
|
963
|
+
},
|
|
701
964
|
])
|
|
702
965
|
|
|
703
966
|
/** A differently shaped workspace with app, browser, and worker environments but no core. */
|