@bleedingdev/modern-js-code-tools 3.9.0-ultramodern.22 → 3.9.0-ultramodern.24

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,9 +3,13 @@ import traverse, { Hub, NodePath } from "@babel/traverse";
3
3
  import * as __rspack_external__babel_types_6e8337dd from "@babel/types";
4
4
  const consumerParserPlugins = (filePath)=>/\.[jt]sx$/u.test(filePath) ? [
5
5
  "typescript",
6
+ 'decorators',
7
+ 'decoratorAutoAccessors',
6
8
  'jsx'
7
9
  ] : [
8
- "typescript"
10
+ "typescript",
11
+ 'decorators',
12
+ 'decoratorAutoAccessors'
9
13
  ];
10
14
  class SourceSyntaxError extends Error {
11
15
  }
@@ -38,7 +42,7 @@ function traverseSource(file, visitor) {
38
42
  traverse(file, visitor, program.scope, void 0, program);
39
43
  }
40
44
  function unwrapExpression(node, includeAssertions = false) {
41
- while(__rspack_external__babel_types_6e8337dd.isTSAsExpression(node) || __rspack_external__babel_types_6e8337dd.isParenthesizedExpression(node) || __rspack_external__babel_types_6e8337dd.isTSSatisfiesExpression(node) || includeAssertions && (__rspack_external__babel_types_6e8337dd.isTSTypeAssertion(node) || __rspack_external__babel_types_6e8337dd.isTSNonNullExpression(node)))node = node.expression;
45
+ while(__rspack_external__babel_types_6e8337dd.isTSAsExpression(node) || __rspack_external__babel_types_6e8337dd.isParenthesizedExpression(node) || __rspack_external__babel_types_6e8337dd.isTSSatisfiesExpression(node) || includeAssertions && (__rspack_external__babel_types_6e8337dd.isTSTypeAssertion(node) || __rspack_external__babel_types_6e8337dd.isTSNonNullExpression(node) || __rspack_external__babel_types_6e8337dd.isTSInstantiationExpression(node)))node = node.expression;
42
46
  return node;
43
47
  }
44
48
  export { SourceSyntaxError, consumerParserPlugins, parseSource, traverseSource, unwrapExpression };
@@ -7,9 +7,9 @@ const flags = [
7
7
  '--baseline-package-directory',
8
8
  '--rules'
9
9
  ];
10
- async function loadContractRules(file) {
10
+ async function loadSourceRules(file) {
11
11
  const rules = (await import(pathToFileURL(node_path.resolve(file)).href)).default;
12
- if (!Array.isArray(rules) || !rules.every((rule)=>'function' == typeof rule)) throw new Error(`${file}: default export must be an array of contract rule functions, e.g. \`export default [myRule]\``);
12
+ if (!Array.isArray(rules) || !rules.every((rule)=>'function' == typeof rule)) throw new Error(`${file}: default export must be an array of source rule functions, e.g. \`export default [myRule]\``);
13
13
  return rules;
14
14
  }
15
15
  async function runMicroVerticalApiCheckCli(args = process.argv.slice(2), filesOnly = false) {
@@ -19,7 +19,7 @@ async function runMicroVerticalApiCheckCli(args = process.argv.slice(2), filesOn
19
19
  for(let index = 0; index < args.length; index += 1){
20
20
  const argument = args[index];
21
21
  if ('--help' === argument || '-h' === argument) {
22
- console.log(`${filesOnly ? 'modern-api-check-files' : 'modern-api-check'} [--workspace-root <path>] [--baseline-package-directory <path>] [--rules <module>]...\n${filesOnly ? 'Checks consumer files, contracts, package exports and owner identity. Does not analyze runtime topology; run the strict Effect source phase separately.' : 'Checks consumer files, contracts, package exports, owner identity and runtime topology once per API entry.'}\n--rules loads a module whose default export is an array of contract rules; each rule receives the module graph of every API contract.\nWorkspace defaults to ULTRAMODERN_WORKSPACE_ROOT then cwd. Exit codes: 0 valid, 1 consumer violation, 2 tool/configuration failure.`);
22
+ console.log(`${filesOnly ? 'modern-api-check-files' : 'modern-api-check'} [--workspace-root <path>] [--baseline-package-directory <path>] [--rules <module>]...\n${filesOnly ? 'Checks consumer files, contracts, package exports and owner identity. Does not analyze runtime topology; run the strict Effect source phase separately.' : 'Checks consumer files, contracts, package exports, owner identity and runtime topology once per API entry.'}\n--rules loads a module whose default export is an array of source rules; each rule runs once per workspace source file with the shared module graph.\nWorkspace defaults to ULTRAMODERN_WORKSPACE_ROOT then cwd. Exit codes: 0 valid, 1 consumer violation, 2 tool/configuration failure.`);
23
23
  return 0;
24
24
  }
25
25
  if (!flags.includes(argument ?? '') || !args[index + 1] || args[index + 1]?.startsWith('--')) {
@@ -31,9 +31,9 @@ async function runMicroVerticalApiCheckCli(args = process.argv.slice(2), filesOn
31
31
  else if ('--rules' === argument) ruleFiles.push(value);
32
32
  else baselinePackageDirectory = value;
33
33
  }
34
- const contractRules = [];
34
+ const sourceRules = [];
35
35
  for (const file of ruleFiles)try {
36
- contractRules.push(...await loadContractRules(file));
36
+ sourceRules.push(...await loadSourceRules(file));
37
37
  } catch (error) {
38
38
  console.error(`API tool error: --rules ${error instanceof Error ? error.message : String(error)}`);
39
39
  return 2;
@@ -41,7 +41,7 @@ async function runMicroVerticalApiCheckCli(args = process.argv.slice(2), filesOn
41
41
  const result = (filesOnly ? checkMicroVerticalApiConsumerFiles : checkMicroVerticalApiBoundaries)({
42
42
  workspaceRoot,
43
43
  baselinePackageDirectory,
44
- contractRules
44
+ sourceRules
45
45
  });
46
46
  for (const diagnostic of result.diagnostics)console.error(`API violation: ${diagnostic}`);
47
47
  for (const error of result.toolErrors)console.error(`API tool error: ${error}`);
@@ -454,7 +454,13 @@ const configuredMicroVerticalApiStem = (verticalPath, verticals)=>{
454
454
  };
455
455
  const microVerticalApiBaselineViolation = (stem, filePath, expectation)=>{
456
456
  try {
457
- const graph = createModuleGraph(filePath);
457
+ const modules = createModuleGraph();
458
+ const graph = {
459
+ ...modules,
460
+ root: modules.module(filePath)
461
+ };
462
+ const [mutation] = graph.root.mutations;
463
+ if (mutation) return `MicroVertical root contract must be valid TypeScript syntax (contract bindings must be immutable at ${mutation.start})`;
458
464
  if (!baselinePublicIdentityIsExact(filePath, expectation)) return 'MicroVertical baseline imports must resolve the exact framework owner public export and schema identity';
459
465
  return validateParsedContract(graph, stem, expectation);
460
466
  } catch (error) {
@@ -409,27 +409,6 @@ function check(options, topology) {
409
409
  const violation = microVerticalApiBaselineViolation(stem, absolute(contract), expectation);
410
410
  if (violation) diagnostics.push(`${contract}: ${violation}`);
411
411
  }
412
- if (options.contractRules?.length && exists(contract)) {
413
- let graph;
414
- try {
415
- graph = createModuleGraph(absolute(contract));
416
- } catch (error) {
417
- if (!(error instanceof SourceSyntaxError)) throw error;
418
- diagnostics.push(`${contract}: contract rules need a parseable contract with immutable bindings (${error.message})`);
419
- }
420
- if (graph) {
421
- const context = {
422
- appPath,
423
- protocol: rpc ? 'rpc' : 'rest',
424
- graph
425
- };
426
- for (const rule of options.contractRules)guarded(`${contract} (rule ${rule.name || 'anonymous'})`, ()=>{
427
- const messages = rule(context);
428
- if (!Array.isArray(messages) || !messages.every((message)=>'string' == typeof message)) throw new Error('contract rule must return an array of violation messages (return [] when the contract passes)');
429
- for (const message of messages)diagnostics.push(`${contract}: ${message}`);
430
- });
431
- }
432
- }
433
412
  if (client) {
434
413
  const relativeContract = node_path.posix.relative(node_path.posix.dirname(client), contract);
435
414
  moduleShape(client, [
@@ -489,6 +468,27 @@ function check(options, topology) {
489
468
  ]
490
469
  ]);
491
470
  });
471
+ const sourceRules = options.sourceRules ?? [];
472
+ const graph = createModuleGraph();
473
+ for (const file of sourceRules.length ? sourceFiles : [])if (/\.[cm]?[jt]sx?$/u.test(file)) guarded(file, ()=>{
474
+ let module;
475
+ try {
476
+ module = graph.module(absolute(file));
477
+ } catch (error) {
478
+ if (!(error instanceof SourceSyntaxError)) throw error;
479
+ diagnostics.push(`${file}: source rules need parseable source (${error.message})`);
480
+ return;
481
+ }
482
+ for (const rule of sourceRules){
483
+ const messages = rule({
484
+ file,
485
+ module,
486
+ graph
487
+ });
488
+ if (!Array.isArray(messages) || !messages.every((message)=>'string' == typeof message)) throw new Error(`rule ${rule.name || 'anonymous'} must return an array of violation messages (return [] when the file passes)`);
489
+ for (const message of messages)diagnostics.push(`${file}: ${message}`);
490
+ }
491
+ });
492
492
  const shell = 'apps/shell-super-app';
493
493
  const verticals = apps.filter((app)=>app.path.startsWith('verticals/') && apiApp(app));
494
494
  if (exists(shell) && verticals.length) {