@kosdev-code/kos-codegen-core 3.0.7 → 3.0.9
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/index.d.ts +2 -2
- package/index.d.ts.map +1 -1
- package/index.js +816 -488
- package/index.js.map +1 -1
- package/index.mjs +815 -487
- package/index.mjs.map +1 -1
- package/lib/generators/add-future-to-model/model-transformer.d.ts +3 -4
- package/lib/generators/add-future-to-model/model-transformer.d.ts.map +1 -1
- package/lib/generators/add-future-to-model/normalize-options.d.ts.map +1 -1
- package/lib/generators/add-future-to-model/registration-transformer.d.ts.map +1 -1
- package/lib/generators/augment/resolve-model-file.d.ts.map +1 -1
- package/lib/generators/ci-sync.d.ts +26 -0
- package/lib/generators/ci-sync.d.ts.map +1 -0
- package/lib/generators/generate-polyglot-workspace.d.ts +38 -0
- package/lib/generators/generate-polyglot-workspace.d.ts.map +1 -0
- package/lib/generators/index.d.ts +6 -0
- package/lib/generators/index.d.ts.map +1 -1
- package/lib/generators/java-workspace.d.ts +33 -0
- package/lib/generators/java-workspace.d.ts.map +1 -0
- package/lib/generators/kab-targets.d.ts +40 -0
- package/lib/generators/kab-targets.d.ts.map +1 -0
- package/lib/generators/release-version-script.d.ts +10 -0
- package/lib/generators/release-version-script.d.ts.map +1 -0
- package/package.json +2 -2
- package/templates/polyglot-workspace/build/build-java.sh.template +18 -0
- package/templates/polyglot-workspace/build/build-release.sh.template +11 -0
- package/templates/polyglot-workspace/build/build-ui.sh.template +23 -0
- package/templates/polyglot-workspace/build/docker-build.sh.template +55 -0
- package/templates/polyglot-workspace/build/jdkw.sh.template +93 -0
- package/templates/polyglot-workspace/build/nodew.sh.template +73 -0
- package/templates/polyglot-workspace/build/release_version_prebuild.sh.template +34 -0
- package/templates/polyglot-workspace/github/build-java.json.template +6 -0
- package/templates/polyglot-workspace/github/build-release.json.template +14 -0
- package/templates/polyglot-workspace/github/build-ui.json.template +13 -0
- package/templates/polyglot-workspace/github/workflows/develop-java.yml.template +35 -0
- package/templates/polyglot-workspace/github/workflows/develop-ui.yml.template +38 -0
- package/templates/polyglot-workspace/github/workflows/release.yml.template +37 -0
- package/templates/polyglot-workspace/java/README.md.template +36 -0
- package/templates/polyglot-workspace/root/.gitignore.template +5 -0
- package/templates/polyglot-workspace/root/README.md.template +71 -0
package/index.js
CHANGED
|
@@ -537,6 +537,296 @@ function generateInit(codegenFs, options) {
|
|
|
537
537
|
};
|
|
538
538
|
writeJson(codegenFs, "nx.json", nxConfig);
|
|
539
539
|
}
|
|
540
|
+
const BUILD_SCRIPTS = [
|
|
541
|
+
"build/build-ui.sh",
|
|
542
|
+
"build/build-java.sh",
|
|
543
|
+
"build/build-release.sh",
|
|
544
|
+
"build/release_version_prebuild.sh",
|
|
545
|
+
"build/docker-build.sh"
|
|
546
|
+
];
|
|
547
|
+
function generatePolyglotWorkspace(codegenFs, templateDir, options) {
|
|
548
|
+
const hasUi = (options.type ?? "polyglot") !== "java";
|
|
549
|
+
const normalized = normalizeAllValues({ name: options.name });
|
|
550
|
+
const vars = {
|
|
551
|
+
...normalized,
|
|
552
|
+
hasUi,
|
|
553
|
+
keyset: options.keyset || "prod.kos",
|
|
554
|
+
appUiName: `${normalized.nameDashCase}-ui`,
|
|
555
|
+
nodeVersion: options.nodeVersion || process.version.replace(/^v/, ""),
|
|
556
|
+
jdkMajor: options.jdkMajor || "17",
|
|
557
|
+
mavenVersion: options.mavenVersion || "3.9.9"
|
|
558
|
+
};
|
|
559
|
+
generateFilesFromTemplates(
|
|
560
|
+
codegenFs,
|
|
561
|
+
path__namespace.join(templateDir, "root"),
|
|
562
|
+
".",
|
|
563
|
+
vars
|
|
564
|
+
);
|
|
565
|
+
generateFilesFromTemplates(
|
|
566
|
+
codegenFs,
|
|
567
|
+
path__namespace.join(templateDir, "build"),
|
|
568
|
+
"build",
|
|
569
|
+
vars
|
|
570
|
+
);
|
|
571
|
+
generateFilesFromTemplates(
|
|
572
|
+
codegenFs,
|
|
573
|
+
path__namespace.join(templateDir, "github"),
|
|
574
|
+
".github",
|
|
575
|
+
vars
|
|
576
|
+
);
|
|
577
|
+
generateFilesFromTemplates(
|
|
578
|
+
codegenFs,
|
|
579
|
+
path__namespace.join(templateDir, "java"),
|
|
580
|
+
"java",
|
|
581
|
+
vars
|
|
582
|
+
);
|
|
583
|
+
return { executablePaths: [...BUILD_SCRIPTS] };
|
|
584
|
+
}
|
|
585
|
+
const UI_PROJECT_DIRS = [
|
|
586
|
+
"apps",
|
|
587
|
+
"libs",
|
|
588
|
+
"plugins",
|
|
589
|
+
"splash",
|
|
590
|
+
"themes",
|
|
591
|
+
"content",
|
|
592
|
+
"translations"
|
|
593
|
+
];
|
|
594
|
+
function joinArtifactPath(outputPath, fileName) {
|
|
595
|
+
return `${outputPath.replace(/\/+$/, "")}/${fileName}`;
|
|
596
|
+
}
|
|
597
|
+
function discoverUiArtifacts(codegenFs) {
|
|
598
|
+
const artifacts = [];
|
|
599
|
+
for (const dir of UI_PROJECT_DIRS) {
|
|
600
|
+
for (const file of codegenFs.listFiles(`ui/${dir}`)) {
|
|
601
|
+
if (!file.endsWith("project.json")) continue;
|
|
602
|
+
const raw = codegenFs.read(file);
|
|
603
|
+
if (raw === null) continue;
|
|
604
|
+
let project;
|
|
605
|
+
try {
|
|
606
|
+
project = JSON.parse(raw);
|
|
607
|
+
} catch {
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
const name = project.name;
|
|
611
|
+
if (!name) continue;
|
|
612
|
+
const kabOptions = project.targets?.kab?.options;
|
|
613
|
+
if (kabOptions?.outputPath && kabOptions?.kabName) {
|
|
614
|
+
artifacts.push({
|
|
615
|
+
id: name,
|
|
616
|
+
filename: `ui/${joinArtifactPath(kabOptions.outputPath, kabOptions.kabName)}`
|
|
617
|
+
});
|
|
618
|
+
} else if (project.targets?.splash) {
|
|
619
|
+
artifacts.push({
|
|
620
|
+
id: name,
|
|
621
|
+
filename: `ui/dist/archives/packages/${name}/${name}.kab`,
|
|
622
|
+
layer: 1
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
for (const file of codegenFs.listFiles("ui/external")) {
|
|
628
|
+
if (!file.endsWith(".kab")) continue;
|
|
629
|
+
const base = file.slice(file.lastIndexOf("/") + 1, -".kab".length);
|
|
630
|
+
artifacts.push({ id: base, filename: file });
|
|
631
|
+
}
|
|
632
|
+
return artifacts;
|
|
633
|
+
}
|
|
634
|
+
function discoverJavaArtifacts(codegenFs) {
|
|
635
|
+
const aggregator = codegenFs.read("java/pom.xml");
|
|
636
|
+
if (aggregator === null) return [];
|
|
637
|
+
const artifacts = [];
|
|
638
|
+
for (const match of aggregator.matchAll(/<module>([^<]+)<\/module>/g)) {
|
|
639
|
+
const moduleDir = match[1];
|
|
640
|
+
const pom = codegenFs.read(`java/${moduleDir}/pom.xml`);
|
|
641
|
+
if (pom === null || !pom.includes("kos-kab-maven-plugin")) continue;
|
|
642
|
+
const withoutParent = pom.replace(/<parent>[\s\S]*?<\/parent>/, "");
|
|
643
|
+
const artifactId = withoutParent.match(
|
|
644
|
+
/<artifactId>([^<]+)<\/artifactId>/
|
|
645
|
+
)?.[1];
|
|
646
|
+
if (!artifactId) continue;
|
|
647
|
+
artifacts.push({
|
|
648
|
+
id: moduleDir,
|
|
649
|
+
// eslint-disable-next-line no-template-curly-in-string
|
|
650
|
+
filename: `java/${moduleDir}/target/${artifactId}-\${KOS_STD_VERSION_REGEX}.kab`
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
return artifacts;
|
|
654
|
+
}
|
|
655
|
+
function syncManifest(codegenFs, manifestPath, discovered, prune) {
|
|
656
|
+
const raw = codegenFs.read(manifestPath);
|
|
657
|
+
if (raw === null) return null;
|
|
658
|
+
const manifest = JSON.parse(raw);
|
|
659
|
+
const existing = manifest.artifacts ?? [];
|
|
660
|
+
const byFilename = new Map(discovered.map((d) => [d.filename, d]));
|
|
661
|
+
const kept = [];
|
|
662
|
+
const stale = [];
|
|
663
|
+
const pruned = [];
|
|
664
|
+
for (const entry of existing) {
|
|
665
|
+
if (byFilename.has(entry.filename)) {
|
|
666
|
+
kept.push(entry);
|
|
667
|
+
byFilename.delete(entry.filename);
|
|
668
|
+
} else if (prune) {
|
|
669
|
+
pruned.push(entry.id ?? entry.filename);
|
|
670
|
+
} else {
|
|
671
|
+
kept.push(entry);
|
|
672
|
+
stale.push(entry.id ?? entry.filename);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
const added = [];
|
|
676
|
+
for (const artifact of byFilename.values()) {
|
|
677
|
+
kept.push({
|
|
678
|
+
id: artifact.id,
|
|
679
|
+
filename: artifact.filename,
|
|
680
|
+
artifactstore: "kos-cdn",
|
|
681
|
+
marketplace: 1,
|
|
682
|
+
...artifact.layer !== void 0 ? { layer: artifact.layer } : {}
|
|
683
|
+
});
|
|
684
|
+
added.push(artifact.id);
|
|
685
|
+
}
|
|
686
|
+
if (added.length > 0 || pruned.length > 0) {
|
|
687
|
+
manifest.artifacts = kept;
|
|
688
|
+
codegenFs.write(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
|
|
689
|
+
}
|
|
690
|
+
return { added, stale, pruned };
|
|
691
|
+
}
|
|
692
|
+
function syncCiManifests(codegenFs, options = {}) {
|
|
693
|
+
const prune = options.prune ?? false;
|
|
694
|
+
const ui = discoverUiArtifacts(codegenFs);
|
|
695
|
+
const java = discoverJavaArtifacts(codegenFs);
|
|
696
|
+
const manifests = {};
|
|
697
|
+
const plan = [
|
|
698
|
+
[".github/build-ui.json", ui],
|
|
699
|
+
[".github/build-java.json", java],
|
|
700
|
+
[".github/build-release.json", [...ui, ...java]]
|
|
701
|
+
];
|
|
702
|
+
for (const [manifestPath, discovered] of plan) {
|
|
703
|
+
const result = syncManifest(codegenFs, manifestPath, discovered, prune);
|
|
704
|
+
if (result) {
|
|
705
|
+
manifests[manifestPath] = result;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
return { manifests, discovered: { ui, java } };
|
|
709
|
+
}
|
|
710
|
+
function ensureJavaAggregatorPom(codegenFs, options) {
|
|
711
|
+
const pomPath = "java/pom.xml";
|
|
712
|
+
const existing = codegenFs.read(pomPath);
|
|
713
|
+
if (existing === null) {
|
|
714
|
+
codegenFs.write(
|
|
715
|
+
pomPath,
|
|
716
|
+
`<?xml version="1.0" encoding="UTF-8"?>
|
|
717
|
+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
718
|
+
<modelVersion>4.0.0</modelVersion>
|
|
719
|
+
<groupId>${options.groupId}</groupId>
|
|
720
|
+
<artifactId>${options.artifactId}</artifactId>
|
|
721
|
+
<version>0.0.0-SNAPSHOT</version>
|
|
722
|
+
<packaging>pom</packaging>
|
|
723
|
+
<modules>
|
|
724
|
+
<module>${options.moduleName}</module>
|
|
725
|
+
</modules>
|
|
726
|
+
</project>
|
|
727
|
+
`
|
|
728
|
+
);
|
|
729
|
+
return;
|
|
730
|
+
}
|
|
731
|
+
if (existing.includes(`<module>${options.moduleName}</module>`)) {
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
codegenFs.write(
|
|
735
|
+
pomPath,
|
|
736
|
+
existing.replace(
|
|
737
|
+
"</modules>",
|
|
738
|
+
` <module>${options.moduleName}</module>
|
|
739
|
+
</modules>`
|
|
740
|
+
)
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
function finalizeArchetypeModule(codegenFs, options) {
|
|
744
|
+
const { moduleName, kosVersion, kabPluginVersion, apiInfoVersion } = options;
|
|
745
|
+
const notes = [];
|
|
746
|
+
const pomPath = `java/${moduleName}/pom.xml`;
|
|
747
|
+
let pom = codegenFs.read(pomPath);
|
|
748
|
+
if (pom === null) {
|
|
749
|
+
throw new Error(`Generated module pom not found: ${pomPath}`);
|
|
750
|
+
}
|
|
751
|
+
if (pom.includes("<kos.version>0.0.0-SNAPSHOT</kos.version>")) {
|
|
752
|
+
if (kosVersion) {
|
|
753
|
+
pom = pom.replace(
|
|
754
|
+
"<kos.version>0.0.0-SNAPSHOT</kos.version>",
|
|
755
|
+
`<kos.version>${kosVersion}</kos.version>`
|
|
756
|
+
);
|
|
757
|
+
} else {
|
|
758
|
+
notes.push(
|
|
759
|
+
"kos.version is 0.0.0-SNAPSHOT — set it to a released kos-bom version before building"
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
if (pom.includes("${kos-kab-maven-plugin.version}") && !pom.includes("<kos-kab-maven-plugin.version>")) {
|
|
764
|
+
if (kabPluginVersion) {
|
|
765
|
+
pom = pom.replace(
|
|
766
|
+
/(<kos\.version>[^<]*<\/kos\.version>)/,
|
|
767
|
+
`$1
|
|
768
|
+
<kos-kab-maven-plugin.version>${kabPluginVersion}</kos-kab-maven-plugin.version>`
|
|
769
|
+
);
|
|
770
|
+
} else {
|
|
771
|
+
notes.push(
|
|
772
|
+
"the ${kos-kab-maven-plugin.version} property is referenced but undefined — add it before building"
|
|
773
|
+
);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
const unversionedApiInfo = /<artifactId>api-info<\/artifactId>(\s*)<\/dependency>/;
|
|
777
|
+
if (unversionedApiInfo.test(pom)) {
|
|
778
|
+
if (apiInfoVersion) {
|
|
779
|
+
pom = pom.replace(
|
|
780
|
+
unversionedApiInfo,
|
|
781
|
+
`<artifactId>api-info</artifactId>$1 <version>${apiInfoVersion}</version>$1</dependency>`
|
|
782
|
+
);
|
|
783
|
+
} else {
|
|
784
|
+
notes.push(
|
|
785
|
+
"api-info has no version and is not managed by the kos-bom — add one before building"
|
|
786
|
+
);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
codegenFs.write(pomPath, pom);
|
|
790
|
+
const githubDir = `java/${moduleName}/github`;
|
|
791
|
+
if (codegenFs.exists(githubDir)) {
|
|
792
|
+
for (const file of codegenFs.listFiles(githubDir)) {
|
|
793
|
+
codegenFs.delete(file);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
const gitignore = codegenFs.read(`java/${moduleName}/gitignore`);
|
|
797
|
+
if (gitignore !== null) {
|
|
798
|
+
codegenFs.write(`java/${moduleName}/.gitignore`, gitignore);
|
|
799
|
+
codegenFs.delete(`java/${moduleName}/gitignore`);
|
|
800
|
+
}
|
|
801
|
+
return notes;
|
|
802
|
+
}
|
|
803
|
+
function addJavaArtifactToManifests(codegenFs, options) {
|
|
804
|
+
const { moduleName } = options;
|
|
805
|
+
for (const manifestPath of [
|
|
806
|
+
".github/build-java.json",
|
|
807
|
+
".github/build-release.json"
|
|
808
|
+
]) {
|
|
809
|
+
const raw = codegenFs.read(manifestPath);
|
|
810
|
+
if (raw === null) {
|
|
811
|
+
continue;
|
|
812
|
+
}
|
|
813
|
+
const manifest = JSON.parse(raw);
|
|
814
|
+
manifest.artifacts = manifest.artifacts ?? [];
|
|
815
|
+
if (manifest.artifacts.some(
|
|
816
|
+
(artifact) => artifact.id === moduleName
|
|
817
|
+
)) {
|
|
818
|
+
continue;
|
|
819
|
+
}
|
|
820
|
+
manifest.artifacts.push({
|
|
821
|
+
id: moduleName,
|
|
822
|
+
// eslint-disable-next-line no-template-curly-in-string
|
|
823
|
+
filename: `java/${moduleName}/target/${moduleName}-\${KOS_STD_VERSION_REGEX}.kab`,
|
|
824
|
+
artifactstore: "kos-cdn",
|
|
825
|
+
marketplace: 1
|
|
826
|
+
});
|
|
827
|
+
codegenFs.write(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
|
|
828
|
+
}
|
|
829
|
+
}
|
|
540
830
|
function normalizeOptions(codegenFs, options, projects) {
|
|
541
831
|
const toNormalize = {
|
|
542
832
|
name: options.name
|
|
@@ -582,6 +872,103 @@ function normalizeOptions(codegenFs, options, projects) {
|
|
|
582
872
|
template: ""
|
|
583
873
|
};
|
|
584
874
|
}
|
|
875
|
+
const UPDATE_RELEASE_VERSION_SCRIPT_PATH = "tools/scripts/update-release-version.mjs";
|
|
876
|
+
function buildKabTargets(options) {
|
|
877
|
+
const {
|
|
878
|
+
name,
|
|
879
|
+
archiveDir = "packages",
|
|
880
|
+
descriptorDir,
|
|
881
|
+
buildTarget = "build"
|
|
882
|
+
} = options;
|
|
883
|
+
const outputPath = `dist/archives/${archiveDir}/${name}/`;
|
|
884
|
+
const targets = {
|
|
885
|
+
kab: {
|
|
886
|
+
command: `node tools/scripts/kabtool.mjs build ${name} && node tools/scripts/kabtool.mjs list ${name} `,
|
|
887
|
+
options: {
|
|
888
|
+
outputPath,
|
|
889
|
+
zipName: "ui.zip",
|
|
890
|
+
kabName: `${name}.kab`
|
|
891
|
+
},
|
|
892
|
+
dependsOn: ["zip"]
|
|
893
|
+
},
|
|
894
|
+
zip: {
|
|
895
|
+
command: `node tools/scripts/archiver.js ${name}`,
|
|
896
|
+
options: {
|
|
897
|
+
outputPath,
|
|
898
|
+
zipName: "ui.zip"
|
|
899
|
+
},
|
|
900
|
+
dependsOn: descriptorDir ? [buildTarget, "descriptor"] : [buildTarget]
|
|
901
|
+
}
|
|
902
|
+
};
|
|
903
|
+
if (descriptorDir) {
|
|
904
|
+
targets.descriptor = {
|
|
905
|
+
command: `node tools/scripts/descriptor.mjs ${name}`,
|
|
906
|
+
options: {
|
|
907
|
+
outputPath: `dist/${descriptorDir}`,
|
|
908
|
+
fileName: "descriptor.json"
|
|
909
|
+
},
|
|
910
|
+
dependsOn: ["build"]
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
targets.version = {
|
|
914
|
+
command: `node ${UPDATE_RELEASE_VERSION_SCRIPT_PATH} ${name} {args.ver}`,
|
|
915
|
+
options: {},
|
|
916
|
+
dependsOn: []
|
|
917
|
+
};
|
|
918
|
+
return targets;
|
|
919
|
+
}
|
|
920
|
+
const UPDATE_RELEASE_VERSION_SCRIPT = `import devkit from "@nx/devkit";
|
|
921
|
+
import { resolve } from "path";
|
|
922
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
923
|
+
import prettier from "prettier";
|
|
924
|
+
|
|
925
|
+
// KOS artifact versioning: stamps the project's .kos.json "version" field
|
|
926
|
+
// (which kabtool bakes into the KAB). Never touches package.json.
|
|
927
|
+
// Driven by tag-based releases:
|
|
928
|
+
// nx run-many --target=version --args=--ver=$KOSBUILD_VERSION
|
|
929
|
+
|
|
930
|
+
const { readCachedProjectGraph } = devkit;
|
|
931
|
+
const [, , name, versionArg] = process.argv;
|
|
932
|
+
|
|
933
|
+
// "{args.ver}" arrives literally when the target runs without --args=--ver=<v>;
|
|
934
|
+
// treat that (or a missing arg) as "report current version, change nothing".
|
|
935
|
+
const version =
|
|
936
|
+
versionArg && !versionArg.startsWith("{args") ? versionArg : undefined;
|
|
937
|
+
|
|
938
|
+
if (!name) {
|
|
939
|
+
console.error("usage: update-release-version.mjs <project> <version>");
|
|
940
|
+
process.exit(1);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
const graph = readCachedProjectGraph();
|
|
944
|
+
const project = graph.nodes[name];
|
|
945
|
+
if (!project) {
|
|
946
|
+
console.error("Unknown project: " + name);
|
|
947
|
+
process.exit(1);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
const kosJsonPath = resolve(process.cwd(), project.data.root, ".kos.json");
|
|
951
|
+
let kosJson;
|
|
952
|
+
try {
|
|
953
|
+
kosJson = JSON.parse(readFileSync(kosJsonPath, "utf8"));
|
|
954
|
+
} catch {
|
|
955
|
+
console.error("Missing or invalid .kos.json: " + kosJsonPath);
|
|
956
|
+
process.exit(1);
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
if (!version) {
|
|
960
|
+
console.log(name + ": " + kosJson.version + " (no --ver given; unchanged)");
|
|
961
|
+
process.exit(0);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
const prettierOptions = await prettier.resolveConfig(kosJsonPath);
|
|
965
|
+
const output = await prettier.format(
|
|
966
|
+
JSON.stringify({ ...kosJson, version }, null, 2),
|
|
967
|
+
{ ...prettierOptions, parser: "json" }
|
|
968
|
+
);
|
|
969
|
+
writeFileSync(kosJsonPath, output);
|
|
970
|
+
console.log(name + ": version -> " + version);
|
|
971
|
+
`;
|
|
585
972
|
function appendBarrelExport(codegenFs, indexPath, exportPath) {
|
|
586
973
|
const exportLine = `export * from '${exportPath}'`;
|
|
587
974
|
const content = codegenFs.read(indexPath) ?? "";
|
|
@@ -977,6 +1364,63 @@ function generateModel(params) {
|
|
|
977
1364
|
);
|
|
978
1365
|
}
|
|
979
1366
|
}
|
|
1367
|
+
function resolveModelFilePath(codegenFs, query, projects) {
|
|
1368
|
+
const kosConfig = getKosProjectConfiguration(
|
|
1369
|
+
codegenFs,
|
|
1370
|
+
query.modelProject,
|
|
1371
|
+
projects
|
|
1372
|
+
);
|
|
1373
|
+
const internal = !!kosConfig?.generator?.internal;
|
|
1374
|
+
const project = findProjectByName(
|
|
1375
|
+
codegenFs.root,
|
|
1376
|
+
query.modelProject,
|
|
1377
|
+
projects
|
|
1378
|
+
);
|
|
1379
|
+
const sourceRoot = project ? project.sourceRoot || path__namespace.join(project.root, "src") : void 0;
|
|
1380
|
+
if (query.modelPath) {
|
|
1381
|
+
return { modelFilePath: query.modelPath, internal, sourceRoot };
|
|
1382
|
+
}
|
|
1383
|
+
if (!project) {
|
|
1384
|
+
throw new Error(
|
|
1385
|
+
`Project not found: ${query.modelProject}. Ensure a project.json exists.`
|
|
1386
|
+
);
|
|
1387
|
+
}
|
|
1388
|
+
const { modelNameDashCase } = normalizeAllValues({
|
|
1389
|
+
modelName: query.modelName
|
|
1390
|
+
});
|
|
1391
|
+
const modelLocation = kosConfig?.generator?.defaults?.model?.folder || "";
|
|
1392
|
+
const modelFilePath = path__namespace.join(
|
|
1393
|
+
sourceRoot,
|
|
1394
|
+
modelLocation,
|
|
1395
|
+
modelNameDashCase,
|
|
1396
|
+
`${modelNameDashCase}-model.ts`
|
|
1397
|
+
);
|
|
1398
|
+
if (codegenFs.exists(modelFilePath)) {
|
|
1399
|
+
return { modelFilePath, internal, sourceRoot };
|
|
1400
|
+
}
|
|
1401
|
+
const discovered = findModelFileByName(
|
|
1402
|
+
codegenFs,
|
|
1403
|
+
path__namespace.join(sourceRoot, modelLocation),
|
|
1404
|
+
modelNameDashCase
|
|
1405
|
+
);
|
|
1406
|
+
if (discovered) {
|
|
1407
|
+
return { modelFilePath: discovered, internal, sourceRoot };
|
|
1408
|
+
}
|
|
1409
|
+
return { modelFilePath, internal, sourceRoot };
|
|
1410
|
+
}
|
|
1411
|
+
function findModelFileByName(codegenFs, searchRoot, modelNameDashCase) {
|
|
1412
|
+
const fileName = `${modelNameDashCase}-model.ts`;
|
|
1413
|
+
const candidates = codegenFs.listFiles(searchRoot).filter((f) => path__namespace.basename(f) === fileName).sort();
|
|
1414
|
+
if (candidates.length === 0) return null;
|
|
1415
|
+
if (candidates.length > 1) {
|
|
1416
|
+
throw new Error(
|
|
1417
|
+
`Model name '${modelNameDashCase}' is ambiguous — multiple files match ${fileName}:
|
|
1418
|
+
` + candidates.map((c) => ` - ${c}`).join("\n") + `
|
|
1419
|
+
Pass modelPath to pick one.`
|
|
1420
|
+
);
|
|
1421
|
+
}
|
|
1422
|
+
return candidates[0];
|
|
1423
|
+
}
|
|
980
1424
|
function normalizeAddFutureOptions(codegenFs, options, projects) {
|
|
981
1425
|
const projectConfiguration = findProjectByName(
|
|
982
1426
|
codegenFs.root,
|
|
@@ -1005,9 +1449,12 @@ function normalizeAddFutureOptions(codegenFs, options, projects) {
|
|
|
1005
1449
|
const nameLowerCase = normalizedValues.modelNameLowerCase;
|
|
1006
1450
|
const projectRoot = projectConfiguration.root;
|
|
1007
1451
|
const sourceRoot = projectConfiguration.sourceRoot || path__namespace.join(projectRoot, "src");
|
|
1008
|
-
const
|
|
1009
|
-
|
|
1010
|
-
|
|
1452
|
+
const { modelFilePath } = resolveModelFilePath(
|
|
1453
|
+
codegenFs,
|
|
1454
|
+
{ modelName: options.modelName, modelProject: options.modelProject },
|
|
1455
|
+
projects
|
|
1456
|
+
);
|
|
1457
|
+
const modelDirectory = path__namespace.dirname(modelFilePath);
|
|
1011
1458
|
const servicesDirectory = path__namespace.join(modelDirectory, "services");
|
|
1012
1459
|
const servicesFilePath = codegenFs.exists(servicesDirectory) ? path__namespace.join(servicesDirectory, `${nameDashCase}-services.ts`) : void 0;
|
|
1013
1460
|
const registrationFilePath = path__namespace.join(
|
|
@@ -1030,310 +1477,358 @@ function normalizeAddFutureOptions(codegenFs, options, projects) {
|
|
|
1030
1477
|
internal
|
|
1031
1478
|
};
|
|
1032
1479
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
}
|
|
1038
|
-
codegenFs;
|
|
1039
|
-
options;
|
|
1040
|
-
transform() {
|
|
1041
|
-
const { modelFilePath } = this.options;
|
|
1042
|
-
if (!this.codegenFs.exists(modelFilePath)) {
|
|
1043
|
-
throw new Error(`Model file not found: ${modelFilePath}`);
|
|
1044
|
-
}
|
|
1045
|
-
let content = this.codegenFs.read(modelFilePath);
|
|
1046
|
-
content = this.addESLintDisable(content);
|
|
1047
|
-
content = this.addImports(content);
|
|
1048
|
-
content = this.addServiceImport(content);
|
|
1049
|
-
content = this.addInterfaceMerging(content);
|
|
1050
|
-
content = this.addDecorator(content);
|
|
1051
|
-
content = this.updatePublicType(content);
|
|
1052
|
-
content = this.removeLegacySetup(content);
|
|
1053
|
-
content = this.addFutureMethod(content);
|
|
1054
|
-
if (this.options.futureType === "complete") {
|
|
1055
|
-
content = this.addOnFutureUpdateMethod(content);
|
|
1056
|
-
}
|
|
1057
|
-
this.codegenFs.write(modelFilePath, content);
|
|
1058
|
-
}
|
|
1059
|
-
addESLintDisable(content) {
|
|
1060
|
-
if (content.includes("@typescript-eslint/no-unsafe-declaration-merging")) {
|
|
1061
|
-
return content;
|
|
1062
|
-
}
|
|
1063
|
-
const eslintDisable = "/* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */\n";
|
|
1064
|
-
return eslintDisable + content;
|
|
1065
|
-
}
|
|
1066
|
-
addImports(content) {
|
|
1067
|
-
const { internal, futureType } = this.options;
|
|
1068
|
-
const isComplete = futureType === "complete";
|
|
1069
|
-
const kosModelImportRegex = internal ? /import\s*{\s*([^}]*kosModel[^}]*)\s*}\s*from\s*"\.\.\/\.\.\/\.\.\/core\/core\/decorators"/ : /import\s*{\s*([^}]*kosModel[^}]*)\s*}\s*from\s*"@kosdev-code\/kos-ui-sdk"/;
|
|
1070
|
-
const kosModelMatch = content.match(kosModelImportRegex);
|
|
1071
|
-
if (kosModelMatch) {
|
|
1072
|
-
const existingImportsStr = kosModelMatch[1] || "";
|
|
1073
|
-
const newImports = [
|
|
1074
|
-
"kosFuture",
|
|
1075
|
-
"kosFutureAware",
|
|
1076
|
-
isComplete ? "KosFutureAwareFull" : "KosFutureAwareMinimal"
|
|
1077
|
-
];
|
|
1078
|
-
const existingImports = existingImportsStr.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1079
|
-
const importsToRemove = [
|
|
1080
|
-
"setupCompleteFutureSupport",
|
|
1081
|
-
"setupMinimalFutureSupport"
|
|
1082
|
-
];
|
|
1083
|
-
const cleanedImports = existingImports.filter(
|
|
1084
|
-
(imp) => !importsToRemove.includes(imp)
|
|
1085
|
-
);
|
|
1086
|
-
const importsToAdd = newImports.filter(
|
|
1087
|
-
(imp) => !cleanedImports.includes(imp)
|
|
1088
|
-
);
|
|
1089
|
-
const allImports = [...cleanedImports, ...importsToAdd];
|
|
1090
|
-
const newImportLine = internal ? `import { ${allImports.join(
|
|
1091
|
-
", "
|
|
1092
|
-
)} } from "../../../core/core/decorators"` : `import { ${allImports.join(", ")} } from "@kosdev-code/kos-ui-sdk"`;
|
|
1093
|
-
content = content.replace(kosModelImportRegex, newImportLine);
|
|
1094
|
-
}
|
|
1095
|
-
const typeImportsBase = internal ? "../../../models/types/future-interfaces" : "@kosdev-code/kos-ui-sdk";
|
|
1096
|
-
const futureTypeImports = ["ExternalFutureInterface", "IFutureModel"];
|
|
1097
|
-
const typeImportRegex = internal ? /import type {([^}]*)} from "\.\.\/\.\.\/\.\.\/models\/types\/future-interfaces"/ : /import type {([^}]*)} from "@kosdev-code\/kos-ui-sdk"/;
|
|
1098
|
-
const typeImportMatch = content.match(typeImportRegex);
|
|
1099
|
-
if (typeImportMatch) {
|
|
1100
|
-
const existingTypes = typeImportMatch[1] || "";
|
|
1101
|
-
const existingTypesList = existingTypes.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1102
|
-
const typesToRemove = [
|
|
1103
|
-
"FutureAwareContainer",
|
|
1104
|
-
"FutureHandlerContainer",
|
|
1105
|
-
"FutureStateAccessor",
|
|
1106
|
-
"FutureUpdateHandler"
|
|
1107
|
-
];
|
|
1108
|
-
const cleanedTypes = existingTypesList.filter(
|
|
1109
|
-
(type) => !typesToRemove.includes(type)
|
|
1110
|
-
);
|
|
1111
|
-
const typesToAdd = futureTypeImports.filter(
|
|
1112
|
-
(type) => !cleanedTypes.includes(type)
|
|
1113
|
-
);
|
|
1114
|
-
const allTypes = [...cleanedTypes, ...typesToAdd].join(", ");
|
|
1115
|
-
const newTypeImport = `import type { ${allTypes} } from "${typeImportsBase}"`;
|
|
1116
|
-
content = content.replace(typeImportRegex, newTypeImport);
|
|
1117
|
-
} else {
|
|
1118
|
-
const importLines = content.split("\n");
|
|
1119
|
-
const lastImportIndex = importLines.findLastIndex(
|
|
1120
|
-
(line) => line.trim().startsWith("import")
|
|
1121
|
-
);
|
|
1122
|
-
if (lastImportIndex >= 0) {
|
|
1123
|
-
const newTypeImport = `import type { ${futureTypeImports.join(
|
|
1124
|
-
", "
|
|
1125
|
-
)} } from "${typeImportsBase}";`;
|
|
1126
|
-
importLines.splice(lastImportIndex + 1, 0, newTypeImport);
|
|
1127
|
-
content = importLines.join("\n");
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
return content;
|
|
1131
|
-
}
|
|
1132
|
-
addServiceImport(content) {
|
|
1133
|
-
const { nameProperCase, updateServices } = this.options;
|
|
1134
|
-
if (!updateServices) {
|
|
1135
|
-
return content;
|
|
1136
|
-
}
|
|
1137
|
-
if (content.includes(`${nameProperCase}OperationProgress`)) {
|
|
1138
|
-
return content;
|
|
1139
|
-
}
|
|
1140
|
-
const servicesImportRegex = /import\s*{([^}]*)}\s*from\s*["']\.\/services["'];?/s;
|
|
1141
|
-
const servicesMatch = content.match(servicesImportRegex);
|
|
1142
|
-
if (servicesMatch) {
|
|
1143
|
-
const existingImports = servicesMatch[1];
|
|
1144
|
-
const cleanedImports = existingImports.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1145
|
-
cleanedImports.push(`${nameProperCase}OperationProgress`);
|
|
1146
|
-
const newImport = `import { ${cleanedImports.join(
|
|
1147
|
-
", "
|
|
1148
|
-
)} } from "./services";`;
|
|
1149
|
-
content = content.replace(servicesImportRegex, newImport);
|
|
1150
|
-
} else {
|
|
1151
|
-
const typesImportRegex = /import\s+type\s+{[^}]*}\s+from\s+["']\.\/types["'];?/;
|
|
1152
|
-
const typesMatch = content.match(typesImportRegex);
|
|
1153
|
-
if (typesMatch) {
|
|
1154
|
-
const newImport = `
|
|
1155
|
-
import type { ${nameProperCase}OperationProgress } from "./services";`;
|
|
1156
|
-
content = content.replace(typesMatch[0], typesMatch[0] + newImport);
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
return content;
|
|
1160
|
-
}
|
|
1161
|
-
addInterfaceMerging(content) {
|
|
1162
|
-
const { nameProperCase, futureType } = this.options;
|
|
1163
|
-
const isComplete = futureType === "complete";
|
|
1164
|
-
const interfaceType = isComplete ? "KosFutureAwareFull" : "KosFutureAwareMinimal";
|
|
1165
|
-
const progressType = this.options.updateServices ? `${nameProperCase}OperationProgress` : "Record<string, unknown>";
|
|
1166
|
-
const classRegex = new RegExp(
|
|
1167
|
-
`(@kosModel[^\\n]*\\n)([^\\n]*export\\s+class\\s+${nameProperCase}ModelImpl)`,
|
|
1168
|
-
"m"
|
|
1169
|
-
);
|
|
1170
|
-
const classMatch = content.match(classRegex);
|
|
1171
|
-
if (classMatch) {
|
|
1172
|
-
const interfaceRegex = new RegExp(
|
|
1173
|
-
`interface\\s+${nameProperCase}ModelImpl\\s+extends`
|
|
1174
|
-
);
|
|
1175
|
-
if (!content.match(interfaceRegex)) {
|
|
1176
|
-
const interfaceMerging = `
|
|
1177
|
-
// Interface merging for Future Container type safety
|
|
1178
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
1179
|
-
export interface ${nameProperCase}ModelImpl extends ${interfaceType}<${progressType}> {}
|
|
1180
|
-
|
|
1181
|
-
`;
|
|
1182
|
-
content = content.replace(
|
|
1183
|
-
classMatch[0],
|
|
1184
|
-
interfaceMerging + classMatch[0]
|
|
1185
|
-
);
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
return content;
|
|
1189
|
-
}
|
|
1190
|
-
addDecorator(content) {
|
|
1191
|
-
const { nameProperCase, futureType } = this.options;
|
|
1192
|
-
const isComplete = futureType === "complete";
|
|
1193
|
-
const classRegex = new RegExp(
|
|
1194
|
-
`(@kosModel[^\\n]*\\n)((?:@[^\\n]*\\n)*)([^\\n]*export\\s+class\\s+${nameProperCase}ModelImpl)`,
|
|
1195
|
-
"m"
|
|
1196
|
-
);
|
|
1197
|
-
const classMatch = content.match(classRegex);
|
|
1198
|
-
if (classMatch) {
|
|
1199
|
-
if (!classMatch[2].includes("@kosFutureAware")) {
|
|
1200
|
-
const decoratorOptions = isComplete ? "" : "{ mode: 'minimal' }";
|
|
1201
|
-
const futureDecorator = `@kosFutureAware(${decoratorOptions})
|
|
1202
|
-
`;
|
|
1203
|
-
content = content.replace(
|
|
1204
|
-
classMatch[0],
|
|
1205
|
-
classMatch[1] + classMatch[2] + futureDecorator + classMatch[3]
|
|
1206
|
-
);
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
return content;
|
|
1210
|
-
}
|
|
1211
|
-
updatePublicType(content) {
|
|
1212
|
-
const { nameProperCase, updateServices } = this.options;
|
|
1213
|
-
const progressType = updateServices ? `${nameProperCase}OperationProgress` : "Record<string, unknown>";
|
|
1214
|
-
const typeRegex = new RegExp(
|
|
1215
|
-
`export\\s+type\\s+${nameProperCase}Model\\s*=\\s*PublicModelInterface<${nameProperCase}ModelImpl>([^;]*);`,
|
|
1216
|
-
"s"
|
|
1217
|
-
);
|
|
1218
|
-
const typeMatch = content.match(typeRegex);
|
|
1219
|
-
if (typeMatch) {
|
|
1220
|
-
if (!typeMatch[1].includes("ExternalFutureInterface")) {
|
|
1221
|
-
const newType = `export type ${nameProperCase}Model = PublicModelInterface<${nameProperCase}ModelImpl> & ExternalFutureInterface<${progressType}>;`;
|
|
1222
|
-
content = content.replace(typeMatch[0], newType);
|
|
1223
|
-
}
|
|
1224
|
-
}
|
|
1225
|
-
return content;
|
|
1226
|
-
}
|
|
1227
|
-
removeLegacySetup(content) {
|
|
1228
|
-
const setupRegex = /\s*setup(Complete|Minimal)FutureSupport\(this\);?\s*/g;
|
|
1229
|
-
content = content.replace(setupRegex, "");
|
|
1230
|
-
const propertyRegex = /\s*(public|private|protected)?\s*(declare\s+)?futureHandler[!?]?:\s*FutureAwareContainer[^;]*;\s*/g;
|
|
1231
|
-
content = content.replace(propertyRegex, "");
|
|
1232
|
-
const futurePropertyRegex = /\s*(public|private|protected)?\s*(declare\s+)?future\??:\s*IFutureModel[^;]*;\s*/g;
|
|
1233
|
-
content = content.replace(futurePropertyRegex, "");
|
|
1234
|
-
const implementsRegex = new RegExp(
|
|
1235
|
-
`(implements\\s+[^{]*?)\\s*,?\\s*(FutureUpdateHandler|FutureHandlerContainer|FutureStateAccessor)`,
|
|
1236
|
-
"g"
|
|
1237
|
-
);
|
|
1238
|
-
content = content.replace(implementsRegex, "$1");
|
|
1239
|
-
content = content.replace(/,\s*,/g, ",");
|
|
1240
|
-
content = content.replace(/implements\s*,/g, "implements");
|
|
1241
|
-
return content;
|
|
1480
|
+
function transformSourceFile(codegenFs, filePath, mutate) {
|
|
1481
|
+
const content = codegenFs.read(filePath);
|
|
1482
|
+
if (content === null) {
|
|
1483
|
+
throw new Error(`File not found: ${filePath}`);
|
|
1242
1484
|
}
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1485
|
+
const project = new tsMorph.Project({
|
|
1486
|
+
useInMemoryFileSystem: true,
|
|
1487
|
+
manipulationSettings: {
|
|
1488
|
+
indentationText: tsMorph.IndentationText.TwoSpaces,
|
|
1489
|
+
quoteKind: tsMorph.QuoteKind.Double
|
|
1247
1490
|
}
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
async performLongRunningOperation(): Promise<void> {
|
|
1261
|
-
// TODO: Implement your long-running operation here
|
|
1262
|
-
// This method should use a service that returns a Future for progress tracking
|
|
1263
|
-
|
|
1264
|
-
this.logger.debug(\`Starting long-running operation for \${this.id}\`);
|
|
1265
|
-
|
|
1266
|
-
// Example implementation pattern using services:
|
|
1267
|
-
// import { perform${nameProperCase}Operation } from './services';
|
|
1268
|
-
//
|
|
1269
|
-
// const future = await perform${nameProperCase}Operation();
|
|
1270
|
-
// return this.futureHandler.setFuture(future);
|
|
1271
|
-
|
|
1272
|
-
// Placeholder that doesn't actually do anything
|
|
1273
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
1274
|
-
|
|
1275
|
-
this.logger.debug(\`Completed long-running operation for \${this.id}\`);
|
|
1491
|
+
});
|
|
1492
|
+
const sourceFile = project.createSourceFile(filePath, content, {
|
|
1493
|
+
overwrite: true
|
|
1494
|
+
});
|
|
1495
|
+
mutate(sourceFile);
|
|
1496
|
+
codegenFs.write(filePath, sourceFile.getFullText());
|
|
1497
|
+
}
|
|
1498
|
+
function ensureNamedImport(sourceFile, moduleSpecifier, names) {
|
|
1499
|
+
const decls = sourceFile.getImportDeclarations().filter((d) => d.getModuleSpecifierValue() === moduleSpecifier);
|
|
1500
|
+
const existing = /* @__PURE__ */ new Set();
|
|
1501
|
+
for (const d of decls) {
|
|
1502
|
+
for (const n of d.getNamedImports()) existing.add(n.getName());
|
|
1276
1503
|
}
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
if (lastBraceIndex >= 0) {
|
|
1281
|
-
const updatedContent = classContent.slice(0, lastBraceIndex) + methodCode + classContent.slice(lastBraceIndex);
|
|
1282
|
-
content = content.replace(
|
|
1283
|
-
classMatch[0],
|
|
1284
|
-
`class ${nameProperCase}ModelImpl${classMatch[0].match(/[^{]*/)?.[0]}{${updatedContent}}`
|
|
1285
|
-
);
|
|
1286
|
-
}
|
|
1287
|
-
}
|
|
1288
|
-
return content;
|
|
1504
|
+
let target = decls.find((d) => !d.isTypeOnly());
|
|
1505
|
+
if (!target) {
|
|
1506
|
+
target = sourceFile.addImportDeclaration({ moduleSpecifier });
|
|
1289
1507
|
}
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
return content;
|
|
1295
|
-
}
|
|
1296
|
-
const futureMethodRegex = /@kosFuture\(\)[^}]*}/s;
|
|
1297
|
-
const futureMethodMatch = content.match(futureMethodRegex);
|
|
1298
|
-
if (futureMethodMatch) {
|
|
1299
|
-
const methodCode = `
|
|
1300
|
-
|
|
1301
|
-
/**
|
|
1302
|
-
* Optional: Custom Future update handling
|
|
1303
|
-
* Called whenever the Future state changes (progress, status, completion, etc.)
|
|
1304
|
-
*/
|
|
1305
|
-
onFutureUpdate?(update: IFutureModel<${progressType}>): void {
|
|
1306
|
-
// Add custom Future update logic here
|
|
1307
|
-
// Examples:
|
|
1308
|
-
// - Log progress milestones
|
|
1309
|
-
// - Update derived state based on progress
|
|
1310
|
-
// - Handle specific error conditions
|
|
1311
|
-
// - Trigger notifications at certain thresholds
|
|
1312
|
-
|
|
1313
|
-
this.logger.debug(\`Future update for \${this.id}:\`, {
|
|
1314
|
-
progress: update.progress,
|
|
1315
|
-
status: update.status,
|
|
1316
|
-
endState: update.endState,
|
|
1317
|
-
clientData: update.clientData
|
|
1318
|
-
});
|
|
1319
|
-
}`;
|
|
1320
|
-
content = content.replace(
|
|
1321
|
-
futureMethodMatch[0],
|
|
1322
|
-
futureMethodMatch[0] + methodCode
|
|
1323
|
-
);
|
|
1324
|
-
}
|
|
1325
|
-
return content;
|
|
1508
|
+
for (const { name, isTypeOnly } of names) {
|
|
1509
|
+
if (existing.has(name)) continue;
|
|
1510
|
+
target.addNamedImport({ name, isTypeOnly: !!isTypeOnly });
|
|
1511
|
+
existing.add(name);
|
|
1326
1512
|
}
|
|
1327
1513
|
}
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1514
|
+
function resolveSdkModuleSpecifier(sourceFile) {
|
|
1515
|
+
const decl = sourceFile.getImportDeclarations().find((d) => d.getNamedImports().some((n) => n.getName() === "kosModel"));
|
|
1516
|
+
return decl?.getModuleSpecifierValue() ?? "@kosdev-code/kos-ui-sdk";
|
|
1517
|
+
}
|
|
1518
|
+
function getModelClass(sourceFile, preferName) {
|
|
1519
|
+
const classes = sourceFile.getClasses();
|
|
1520
|
+
const byName = preferName ? classes.find((c) => c.getName() === preferName) : void 0;
|
|
1521
|
+
if (byName) return byName;
|
|
1522
|
+
const impl = classes.find((c) => /ModelImpl$/.test(c.getName() ?? ""));
|
|
1523
|
+
if (impl) return impl;
|
|
1524
|
+
const exported = classes.find((c) => c.isExported());
|
|
1525
|
+
if (exported) return exported;
|
|
1526
|
+
if (classes.length > 0) return classes[0];
|
|
1527
|
+
throw new Error("No class declaration found in model file.");
|
|
1528
|
+
}
|
|
1529
|
+
function addClassDecorator(cls, name, opts) {
|
|
1530
|
+
if (cls.getDecorator(name)) return;
|
|
1531
|
+
const typeArgs = opts?.typeArgs?.length ? `<${opts.typeArgs.join(", ")}>` : "";
|
|
1532
|
+
const args = opts?.argsText ?? "";
|
|
1533
|
+
const decorators = cls.getDecorators();
|
|
1534
|
+
const kosModelIdx = decorators.findIndex((d) => d.getName() === "kosModel");
|
|
1535
|
+
const insertIdx = kosModelIdx >= 0 ? kosModelIdx + 1 : decorators.length;
|
|
1536
|
+
cls.insertDecorator(insertIdx, {
|
|
1537
|
+
name: `${name}${typeArgs}`,
|
|
1538
|
+
arguments: args ? [args] : []
|
|
1539
|
+
});
|
|
1540
|
+
}
|
|
1541
|
+
function ensureDeclarationMerge(sourceFile, interfaceName, extendsExpr, typeParameters = []) {
|
|
1542
|
+
const baseType = extendsExpr.split("<")[0].trim();
|
|
1543
|
+
let iface = sourceFile.getInterface(interfaceName);
|
|
1544
|
+
if (iface) {
|
|
1545
|
+
const already = iface.getExtends().some((e) => e.getText().split("<")[0].trim() === baseType);
|
|
1546
|
+
if (!already) iface.addExtends(extendsExpr);
|
|
1547
|
+
return;
|
|
1548
|
+
}
|
|
1549
|
+
iface = sourceFile.addInterface({
|
|
1550
|
+
name: interfaceName,
|
|
1551
|
+
isExported: true,
|
|
1552
|
+
typeParameters,
|
|
1553
|
+
extends: [extendsExpr]
|
|
1554
|
+
});
|
|
1555
|
+
sourceFile.insertText(
|
|
1556
|
+
iface.getStart(),
|
|
1557
|
+
"// eslint-disable-next-line @typescript-eslint/no-empty-interface\n"
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1560
|
+
function ensureFileEslintDisable(sourceFile, rule) {
|
|
1561
|
+
if (sourceFile.getFullText().includes(rule)) return;
|
|
1562
|
+
sourceFile.insertText(0, `/* eslint-disable ${rule} */
|
|
1563
|
+
`);
|
|
1564
|
+
}
|
|
1565
|
+
function addDecoratedMethod(cls, spec) {
|
|
1566
|
+
if (cls.getMethod(spec.name)) return false;
|
|
1567
|
+
cls.addMethod({
|
|
1568
|
+
name: spec.name,
|
|
1569
|
+
isAsync: spec.isAsync,
|
|
1570
|
+
returnType: spec.returnType,
|
|
1571
|
+
parameters: spec.parameters?.map((p) => {
|
|
1572
|
+
return { name: p.name, type: p.type };
|
|
1573
|
+
}),
|
|
1574
|
+
statements: spec.statements,
|
|
1575
|
+
decorators: [
|
|
1576
|
+
{
|
|
1577
|
+
name: spec.decoratorName,
|
|
1578
|
+
arguments: spec.decoratorArgsText ? [spec.decoratorArgsText] : []
|
|
1579
|
+
}
|
|
1580
|
+
]
|
|
1581
|
+
});
|
|
1582
|
+
return true;
|
|
1583
|
+
}
|
|
1584
|
+
function propertyInsertIndex(cls) {
|
|
1585
|
+
const props = cls.getProperties();
|
|
1586
|
+
if (props.length === 0) return 0;
|
|
1587
|
+
return props[props.length - 1].getChildIndex() + 1;
|
|
1588
|
+
}
|
|
1589
|
+
function addPlainProperty(cls, spec) {
|
|
1590
|
+
if (cls.getProperty(spec.name)) return false;
|
|
1591
|
+
cls.insertProperty(propertyInsertIndex(cls), {
|
|
1592
|
+
name: spec.name,
|
|
1593
|
+
type: spec.type,
|
|
1594
|
+
initializer: spec.initializer,
|
|
1595
|
+
isReadonly: !!spec.readonly,
|
|
1596
|
+
scope: spec.scope ? SCOPE_MAP[spec.scope] : void 0,
|
|
1597
|
+
hasExclamationToken: spec.initializer ? false : !!spec.hasExclamation
|
|
1598
|
+
});
|
|
1599
|
+
return true;
|
|
1600
|
+
}
|
|
1601
|
+
const SCOPE_MAP = {
|
|
1602
|
+
private: tsMorph.Scope.Private,
|
|
1603
|
+
protected: tsMorph.Scope.Protected,
|
|
1604
|
+
public: tsMorph.Scope.Public
|
|
1605
|
+
};
|
|
1606
|
+
function addDecoratedProperty(cls, spec) {
|
|
1607
|
+
if (cls.getProperty(spec.name)) return false;
|
|
1608
|
+
cls.insertProperty(propertyInsertIndex(cls), {
|
|
1609
|
+
name: spec.name,
|
|
1610
|
+
type: spec.type,
|
|
1611
|
+
initializer: spec.initializer,
|
|
1612
|
+
scope: spec.scope ? SCOPE_MAP[spec.scope] : void 0,
|
|
1613
|
+
hasExclamationToken: spec.initializer ? false : !!spec.hasExclamation,
|
|
1614
|
+
decorators: [
|
|
1615
|
+
spec.bare && !spec.decoratorArgsText ? { name: spec.decoratorName } : {
|
|
1616
|
+
name: spec.decoratorName,
|
|
1617
|
+
arguments: spec.decoratorArgsText ? [spec.decoratorArgsText] : []
|
|
1618
|
+
}
|
|
1619
|
+
]
|
|
1620
|
+
});
|
|
1621
|
+
return true;
|
|
1622
|
+
}
|
|
1623
|
+
function addGetter(cls, spec) {
|
|
1624
|
+
if (cls.getGetAccessor(spec.name)) return false;
|
|
1625
|
+
const ctor = cls.getConstructors()[0];
|
|
1626
|
+
const index = ctor ? ctor.getChildIndex() + 1 : propertyInsertIndex(cls);
|
|
1627
|
+
cls.insertGetAccessor(index, {
|
|
1628
|
+
name: spec.name,
|
|
1629
|
+
returnType: spec.returnType,
|
|
1630
|
+
statements: spec.statements ?? "// TODO: derive and return the computed value"
|
|
1631
|
+
});
|
|
1632
|
+
return true;
|
|
1633
|
+
}
|
|
1634
|
+
const LEGACY_IMPORTS = /* @__PURE__ */ new Set([
|
|
1635
|
+
"setupCompleteFutureSupport",
|
|
1636
|
+
"setupMinimalFutureSupport",
|
|
1637
|
+
"FutureAwareContainer",
|
|
1638
|
+
"FutureHandlerContainer",
|
|
1639
|
+
"FutureStateAccessor",
|
|
1640
|
+
"FutureUpdateHandler"
|
|
1641
|
+
]);
|
|
1642
|
+
const LEGACY_IMPLEMENTS = /* @__PURE__ */ new Set([
|
|
1643
|
+
"FutureUpdateHandler",
|
|
1644
|
+
"FutureHandlerContainer",
|
|
1645
|
+
"FutureStateAccessor"
|
|
1646
|
+
]);
|
|
1647
|
+
class ModelFileTransformer {
|
|
1648
|
+
constructor(codegenFs, options) {
|
|
1649
|
+
this.codegenFs = codegenFs;
|
|
1650
|
+
this.options = options;
|
|
1651
|
+
}
|
|
1652
|
+
codegenFs;
|
|
1653
|
+
options;
|
|
1654
|
+
get progressType() {
|
|
1655
|
+
const { nameProperCase, updateServices } = this.options;
|
|
1656
|
+
return updateServices ? `${nameProperCase}OperationProgress` : "Record<string, unknown>";
|
|
1657
|
+
}
|
|
1658
|
+
transform() {
|
|
1659
|
+
const { modelFilePath } = this.options;
|
|
1660
|
+
if (!this.codegenFs.exists(modelFilePath)) {
|
|
1661
|
+
throw new Error(`Model file not found: ${modelFilePath}`);
|
|
1662
|
+
}
|
|
1663
|
+
transformSourceFile(this.codegenFs, modelFilePath, (sf) => {
|
|
1664
|
+
this.removeLegacyImports(sf);
|
|
1665
|
+
this.addImports(sf);
|
|
1666
|
+
const cls = getModelClass(sf, `${this.options.nameProperCase}ModelImpl`);
|
|
1667
|
+
this.removeLegacyClassMembers(cls);
|
|
1668
|
+
this.addDecorator(cls);
|
|
1669
|
+
this.addFutureMethod(cls);
|
|
1670
|
+
if (this.options.futureType === "complete") {
|
|
1671
|
+
this.addOnFutureUpdateMethod(cls);
|
|
1672
|
+
}
|
|
1673
|
+
this.updatePublicType(sf);
|
|
1674
|
+
ensureDeclarationMerge(
|
|
1675
|
+
sf,
|
|
1676
|
+
`${this.options.nameProperCase}ModelImpl`,
|
|
1677
|
+
`${this.options.futureType === "complete" ? "KosFutureAwareFull" : "KosFutureAwareMinimal"}<${this.progressType}>`
|
|
1678
|
+
);
|
|
1679
|
+
ensureFileEslintDisable(
|
|
1680
|
+
sf,
|
|
1681
|
+
"@typescript-eslint/no-unsafe-declaration-merging"
|
|
1682
|
+
);
|
|
1683
|
+
});
|
|
1684
|
+
}
|
|
1685
|
+
removeLegacyImports(sf) {
|
|
1686
|
+
for (const decl of sf.getImportDeclarations()) {
|
|
1687
|
+
for (const named of decl.getNamedImports()) {
|
|
1688
|
+
if (LEGACY_IMPORTS.has(named.getName())) named.remove();
|
|
1689
|
+
}
|
|
1690
|
+
if (decl.getNamedImports().length === 0 && !decl.getDefaultImport() && !decl.getNamespaceImport()) {
|
|
1691
|
+
decl.remove();
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
addImports(sf) {
|
|
1696
|
+
const { internal, futureType, updateServices, nameProperCase } = this.options;
|
|
1697
|
+
const isComplete = futureType === "complete";
|
|
1698
|
+
const sdkSpec = resolveSdkModuleSpecifier(sf);
|
|
1699
|
+
ensureNamedImport(sf, sdkSpec, [
|
|
1700
|
+
{ name: "kosFuture" },
|
|
1701
|
+
{ name: "kosFutureAware" },
|
|
1702
|
+
{
|
|
1703
|
+
name: isComplete ? "KosFutureAwareFull" : "KosFutureAwareMinimal",
|
|
1704
|
+
isTypeOnly: true
|
|
1705
|
+
}
|
|
1706
|
+
]);
|
|
1707
|
+
const typeSpec = internal ? "../../../models/types/future-interfaces" : sdkSpec;
|
|
1708
|
+
ensureNamedImport(sf, typeSpec, [
|
|
1709
|
+
{ name: "ExternalFutureInterface", isTypeOnly: true },
|
|
1710
|
+
...isComplete ? [{ name: "IFutureModel", isTypeOnly: true }] : []
|
|
1711
|
+
]);
|
|
1712
|
+
if (updateServices) {
|
|
1713
|
+
ensureNamedImport(sf, "./services", [
|
|
1714
|
+
{ name: `${nameProperCase}OperationProgress`, isTypeOnly: true }
|
|
1715
|
+
]);
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
removeLegacyClassMembers(cls) {
|
|
1719
|
+
for (const ctor of cls.getConstructors()) {
|
|
1720
|
+
for (const stmt of ctor.getStatements()) {
|
|
1721
|
+
if (/setup(Complete|Minimal)FutureSupport\s*\(\s*this\s*\)/.test(
|
|
1722
|
+
stmt.getText()
|
|
1723
|
+
)) {
|
|
1724
|
+
stmt.remove();
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
const futureHandler = cls.getProperty("futureHandler");
|
|
1729
|
+
if (futureHandler?.getTypeNode()?.getText().includes("FutureAwareContainer")) {
|
|
1730
|
+
futureHandler.remove();
|
|
1731
|
+
}
|
|
1732
|
+
const future = cls.getProperty("future");
|
|
1733
|
+
if (future?.getTypeNode()?.getText().includes("IFutureModel")) {
|
|
1734
|
+
future.remove();
|
|
1735
|
+
}
|
|
1736
|
+
const impls = cls.getImplements();
|
|
1737
|
+
for (let i = impls.length - 1; i >= 0; i--) {
|
|
1738
|
+
const base = impls[i].getText().split("<")[0].trim();
|
|
1739
|
+
if (LEGACY_IMPLEMENTS.has(base)) cls.removeImplements(i);
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
addDecorator(cls) {
|
|
1743
|
+
const argsText = this.options.futureType === "complete" ? "" : `{ mode: "minimal" }`;
|
|
1744
|
+
addClassDecorator(cls, "kosFutureAware", { argsText });
|
|
1745
|
+
}
|
|
1746
|
+
updatePublicType(sf) {
|
|
1747
|
+
const { nameProperCase } = this.options;
|
|
1748
|
+
const alias = sf.getTypeAlias(`${nameProperCase}Model`);
|
|
1749
|
+
if (!alias) return;
|
|
1750
|
+
const text = alias.getTypeNode()?.getText() ?? "";
|
|
1751
|
+
if (!text.includes(`PublicModelInterface<${nameProperCase}ModelImpl>`) || text.includes("ExternalFutureInterface")) {
|
|
1752
|
+
return;
|
|
1753
|
+
}
|
|
1754
|
+
alias.setType(
|
|
1755
|
+
`PublicModelInterface<${nameProperCase}ModelImpl> & ExternalFutureInterface<${this.progressType}>`
|
|
1756
|
+
);
|
|
1757
|
+
}
|
|
1758
|
+
addFutureMethod(cls) {
|
|
1759
|
+
const { nameProperCase } = this.options;
|
|
1760
|
+
const hasFutureMethod = cls.getMethods().some((m) => m.getDecorator("kosFuture"));
|
|
1761
|
+
if (hasFutureMethod || cls.getMethod("performLongRunningOperation")) return;
|
|
1762
|
+
cls.addMethod({
|
|
1763
|
+
name: "performLongRunningOperation",
|
|
1764
|
+
isAsync: true,
|
|
1765
|
+
returnType: "Promise<void>",
|
|
1766
|
+
decorators: [{ name: "kosFuture", arguments: [] }],
|
|
1767
|
+
docs: [
|
|
1768
|
+
{
|
|
1769
|
+
description: "Placeholder method for Future operations\nReplace this with your actual long-running operation"
|
|
1770
|
+
}
|
|
1771
|
+
],
|
|
1772
|
+
statements: [
|
|
1773
|
+
"// TODO: Implement your long-running operation here",
|
|
1774
|
+
"// This method should use a service that returns a Future for progress tracking",
|
|
1775
|
+
"",
|
|
1776
|
+
"this.logger.debug(`Starting long-running operation for ${this.id}`);",
|
|
1777
|
+
"",
|
|
1778
|
+
"// Example implementation pattern using services:",
|
|
1779
|
+
`// import { perform${nameProperCase}Operation } from './services';`,
|
|
1780
|
+
"//",
|
|
1781
|
+
`// const future = await perform${nameProperCase}Operation();`,
|
|
1782
|
+
"// return this.futureHandler.setFuture(future);",
|
|
1783
|
+
"",
|
|
1784
|
+
"// Placeholder that doesn't actually do anything",
|
|
1785
|
+
"await new Promise((resolve) => setTimeout(resolve, 1000));",
|
|
1786
|
+
"",
|
|
1787
|
+
"this.logger.debug(`Completed long-running operation for ${this.id}`);"
|
|
1788
|
+
]
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
addOnFutureUpdateMethod(cls) {
|
|
1792
|
+
if (cls.getMethod("onFutureUpdate")) return;
|
|
1793
|
+
cls.addMethod({
|
|
1794
|
+
name: "onFutureUpdate",
|
|
1795
|
+
hasQuestionToken: true,
|
|
1796
|
+
returnType: "void",
|
|
1797
|
+
parameters: [
|
|
1798
|
+
{ name: "update", type: `IFutureModel<${this.progressType}>` }
|
|
1799
|
+
],
|
|
1800
|
+
docs: [
|
|
1801
|
+
{
|
|
1802
|
+
description: "Optional: Custom Future update handling\nCalled whenever the Future state changes (progress, status, completion, etc.)"
|
|
1803
|
+
}
|
|
1804
|
+
],
|
|
1805
|
+
statements: [
|
|
1806
|
+
"// Add custom Future update logic here",
|
|
1807
|
+
"// Examples:",
|
|
1808
|
+
"// - Log progress milestones",
|
|
1809
|
+
"// - Update derived state based on progress",
|
|
1810
|
+
"// - Handle specific error conditions",
|
|
1811
|
+
"// - Trigger notifications at certain thresholds",
|
|
1812
|
+
"",
|
|
1813
|
+
"this.logger.debug(`Future update for ${this.id}:`, {",
|
|
1814
|
+
" progress: update.progress,",
|
|
1815
|
+
" status: update.status,",
|
|
1816
|
+
" endState: update.endState,",
|
|
1817
|
+
" clientData: update.clientData,",
|
|
1818
|
+
"});"
|
|
1819
|
+
]
|
|
1820
|
+
});
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
class ServiceFileTransformer {
|
|
1824
|
+
constructor(codegenFs, options) {
|
|
1825
|
+
this.codegenFs = codegenFs;
|
|
1826
|
+
this.options = options;
|
|
1827
|
+
}
|
|
1828
|
+
codegenFs;
|
|
1829
|
+
options;
|
|
1830
|
+
transform() {
|
|
1831
|
+
const { servicesFilePath } = this.options;
|
|
1337
1832
|
if (!servicesFilePath || !this.codegenFs.exists(servicesFilePath)) {
|
|
1338
1833
|
this.createServicesFile();
|
|
1339
1834
|
return;
|
|
@@ -1505,9 +2000,16 @@ class RegistrationFileTransformer {
|
|
|
1505
2000
|
logger.warn("Registration file not found, skipping registration updates");
|
|
1506
2001
|
return;
|
|
1507
2002
|
}
|
|
1508
|
-
|
|
2003
|
+
const original = this.codegenFs.read(registrationFilePath);
|
|
2004
|
+
let content = original;
|
|
1509
2005
|
content = this.addTypeCast(content);
|
|
1510
2006
|
content = this.updateDocumentation(content);
|
|
2007
|
+
if (content === original) {
|
|
2008
|
+
logger.info(
|
|
2009
|
+
"Registration file has no legacy future patterns — leaving it untouched"
|
|
2010
|
+
);
|
|
2011
|
+
return;
|
|
2012
|
+
}
|
|
1511
2013
|
this.codegenFs.write(registrationFilePath, content);
|
|
1512
2014
|
}
|
|
1513
2015
|
addTypeCast(content) {
|
|
@@ -1575,7 +2077,7 @@ function addFutureToModel(codegenFs, options, projects) {
|
|
|
1575
2077
|
}
|
|
1576
2078
|
if (normalized.registrationFilePath) {
|
|
1577
2079
|
logger.info(
|
|
1578
|
-
`Would
|
|
2080
|
+
`Would update registration file (only if legacy patterns are present): ${normalized.registrationFilePath}`
|
|
1579
2081
|
);
|
|
1580
2082
|
}
|
|
1581
2083
|
return;
|
|
@@ -1626,191 +2128,6 @@ function addFutureToModel(codegenFs, options, projects) {
|
|
|
1626
2128
|
throw error;
|
|
1627
2129
|
}
|
|
1628
2130
|
}
|
|
1629
|
-
function resolveModelFilePath(codegenFs, query, projects) {
|
|
1630
|
-
const kosConfig = getKosProjectConfiguration(
|
|
1631
|
-
codegenFs,
|
|
1632
|
-
query.modelProject,
|
|
1633
|
-
projects
|
|
1634
|
-
);
|
|
1635
|
-
const internal = !!kosConfig?.generator?.internal;
|
|
1636
|
-
const project = findProjectByName(
|
|
1637
|
-
codegenFs.root,
|
|
1638
|
-
query.modelProject,
|
|
1639
|
-
projects
|
|
1640
|
-
);
|
|
1641
|
-
const sourceRoot = project ? project.sourceRoot || path__namespace.join(project.root, "src") : void 0;
|
|
1642
|
-
if (query.modelPath) {
|
|
1643
|
-
return { modelFilePath: query.modelPath, internal, sourceRoot };
|
|
1644
|
-
}
|
|
1645
|
-
if (!project) {
|
|
1646
|
-
throw new Error(
|
|
1647
|
-
`Project not found: ${query.modelProject}. Ensure a project.json exists.`
|
|
1648
|
-
);
|
|
1649
|
-
}
|
|
1650
|
-
const { modelNameDashCase } = normalizeAllValues({
|
|
1651
|
-
modelName: query.modelName
|
|
1652
|
-
});
|
|
1653
|
-
const modelLocation = kosConfig?.generator?.defaults?.model?.folder || "";
|
|
1654
|
-
const modelFilePath = path__namespace.join(
|
|
1655
|
-
sourceRoot,
|
|
1656
|
-
modelLocation,
|
|
1657
|
-
modelNameDashCase,
|
|
1658
|
-
`${modelNameDashCase}-model.ts`
|
|
1659
|
-
);
|
|
1660
|
-
return { modelFilePath, internal, sourceRoot };
|
|
1661
|
-
}
|
|
1662
|
-
function transformSourceFile(codegenFs, filePath, mutate) {
|
|
1663
|
-
const content = codegenFs.read(filePath);
|
|
1664
|
-
if (content === null) {
|
|
1665
|
-
throw new Error(`File not found: ${filePath}`);
|
|
1666
|
-
}
|
|
1667
|
-
const project = new tsMorph.Project({
|
|
1668
|
-
useInMemoryFileSystem: true,
|
|
1669
|
-
manipulationSettings: {
|
|
1670
|
-
indentationText: tsMorph.IndentationText.TwoSpaces,
|
|
1671
|
-
quoteKind: tsMorph.QuoteKind.Double
|
|
1672
|
-
}
|
|
1673
|
-
});
|
|
1674
|
-
const sourceFile = project.createSourceFile(filePath, content, {
|
|
1675
|
-
overwrite: true
|
|
1676
|
-
});
|
|
1677
|
-
mutate(sourceFile);
|
|
1678
|
-
codegenFs.write(filePath, sourceFile.getFullText());
|
|
1679
|
-
}
|
|
1680
|
-
function ensureNamedImport(sourceFile, moduleSpecifier, names) {
|
|
1681
|
-
const decls = sourceFile.getImportDeclarations().filter((d) => d.getModuleSpecifierValue() === moduleSpecifier);
|
|
1682
|
-
const existing = /* @__PURE__ */ new Set();
|
|
1683
|
-
for (const d of decls) {
|
|
1684
|
-
for (const n of d.getNamedImports()) existing.add(n.getName());
|
|
1685
|
-
}
|
|
1686
|
-
let target = decls.find((d) => !d.isTypeOnly());
|
|
1687
|
-
if (!target) {
|
|
1688
|
-
target = sourceFile.addImportDeclaration({ moduleSpecifier });
|
|
1689
|
-
}
|
|
1690
|
-
for (const { name, isTypeOnly } of names) {
|
|
1691
|
-
if (existing.has(name)) continue;
|
|
1692
|
-
target.addNamedImport({ name, isTypeOnly: !!isTypeOnly });
|
|
1693
|
-
existing.add(name);
|
|
1694
|
-
}
|
|
1695
|
-
}
|
|
1696
|
-
function resolveSdkModuleSpecifier(sourceFile) {
|
|
1697
|
-
const decl = sourceFile.getImportDeclarations().find((d) => d.getNamedImports().some((n) => n.getName() === "kosModel"));
|
|
1698
|
-
return decl?.getModuleSpecifierValue() ?? "@kosdev-code/kos-ui-sdk";
|
|
1699
|
-
}
|
|
1700
|
-
function getModelClass(sourceFile, preferName) {
|
|
1701
|
-
const classes = sourceFile.getClasses();
|
|
1702
|
-
const impl = classes.find((c) => /ModelImpl$/.test(c.getName() ?? ""));
|
|
1703
|
-
if (impl) return impl;
|
|
1704
|
-
const exported = classes.find((c) => c.isExported());
|
|
1705
|
-
if (exported) return exported;
|
|
1706
|
-
if (classes.length > 0) return classes[0];
|
|
1707
|
-
throw new Error("No class declaration found in model file.");
|
|
1708
|
-
}
|
|
1709
|
-
function addClassDecorator(cls, name, opts) {
|
|
1710
|
-
if (cls.getDecorator(name)) return;
|
|
1711
|
-
const typeArgs = opts?.typeArgs?.length ? `<${opts.typeArgs.join(", ")}>` : "";
|
|
1712
|
-
const args = opts?.argsText ?? "";
|
|
1713
|
-
const decorators = cls.getDecorators();
|
|
1714
|
-
const kosModelIdx = decorators.findIndex((d) => d.getName() === "kosModel");
|
|
1715
|
-
const insertIdx = kosModelIdx >= 0 ? kosModelIdx + 1 : decorators.length;
|
|
1716
|
-
cls.insertDecorator(insertIdx, {
|
|
1717
|
-
name: `${name}${typeArgs}`,
|
|
1718
|
-
arguments: args ? [args] : []
|
|
1719
|
-
});
|
|
1720
|
-
}
|
|
1721
|
-
function ensureDeclarationMerge(sourceFile, interfaceName, extendsExpr, typeParameters = []) {
|
|
1722
|
-
const baseType = extendsExpr.split("<")[0].trim();
|
|
1723
|
-
let iface = sourceFile.getInterface(interfaceName);
|
|
1724
|
-
if (iface) {
|
|
1725
|
-
const already = iface.getExtends().some((e) => e.getText().split("<")[0].trim() === baseType);
|
|
1726
|
-
if (!already) iface.addExtends(extendsExpr);
|
|
1727
|
-
return;
|
|
1728
|
-
}
|
|
1729
|
-
iface = sourceFile.addInterface({
|
|
1730
|
-
name: interfaceName,
|
|
1731
|
-
isExported: true,
|
|
1732
|
-
typeParameters,
|
|
1733
|
-
extends: [extendsExpr]
|
|
1734
|
-
});
|
|
1735
|
-
sourceFile.insertText(
|
|
1736
|
-
iface.getStart(),
|
|
1737
|
-
"// eslint-disable-next-line @typescript-eslint/no-empty-interface\n"
|
|
1738
|
-
);
|
|
1739
|
-
}
|
|
1740
|
-
function ensureFileEslintDisable(sourceFile, rule) {
|
|
1741
|
-
if (sourceFile.getFullText().includes(rule)) return;
|
|
1742
|
-
sourceFile.insertText(0, `/* eslint-disable ${rule} */
|
|
1743
|
-
`);
|
|
1744
|
-
}
|
|
1745
|
-
function addDecoratedMethod(cls, spec) {
|
|
1746
|
-
if (cls.getMethod(spec.name)) return false;
|
|
1747
|
-
cls.addMethod({
|
|
1748
|
-
name: spec.name,
|
|
1749
|
-
isAsync: spec.isAsync,
|
|
1750
|
-
returnType: spec.returnType,
|
|
1751
|
-
parameters: spec.parameters?.map((p) => {
|
|
1752
|
-
return { name: p.name, type: p.type };
|
|
1753
|
-
}),
|
|
1754
|
-
statements: spec.statements,
|
|
1755
|
-
decorators: [
|
|
1756
|
-
{
|
|
1757
|
-
name: spec.decoratorName,
|
|
1758
|
-
arguments: spec.decoratorArgsText ? [spec.decoratorArgsText] : []
|
|
1759
|
-
}
|
|
1760
|
-
]
|
|
1761
|
-
});
|
|
1762
|
-
return true;
|
|
1763
|
-
}
|
|
1764
|
-
function propertyInsertIndex(cls) {
|
|
1765
|
-
const props = cls.getProperties();
|
|
1766
|
-
if (props.length === 0) return 0;
|
|
1767
|
-
return props[props.length - 1].getChildIndex() + 1;
|
|
1768
|
-
}
|
|
1769
|
-
function addPlainProperty(cls, spec) {
|
|
1770
|
-
if (cls.getProperty(spec.name)) return false;
|
|
1771
|
-
cls.insertProperty(propertyInsertIndex(cls), {
|
|
1772
|
-
name: spec.name,
|
|
1773
|
-
type: spec.type,
|
|
1774
|
-
initializer: spec.initializer,
|
|
1775
|
-
isReadonly: !!spec.readonly,
|
|
1776
|
-
scope: spec.scope ? SCOPE_MAP[spec.scope] : void 0,
|
|
1777
|
-
hasExclamationToken: spec.initializer ? false : !!spec.hasExclamation
|
|
1778
|
-
});
|
|
1779
|
-
return true;
|
|
1780
|
-
}
|
|
1781
|
-
const SCOPE_MAP = {
|
|
1782
|
-
private: tsMorph.Scope.Private,
|
|
1783
|
-
protected: tsMorph.Scope.Protected,
|
|
1784
|
-
public: tsMorph.Scope.Public
|
|
1785
|
-
};
|
|
1786
|
-
function addDecoratedProperty(cls, spec) {
|
|
1787
|
-
if (cls.getProperty(spec.name)) return false;
|
|
1788
|
-
cls.insertProperty(propertyInsertIndex(cls), {
|
|
1789
|
-
name: spec.name,
|
|
1790
|
-
type: spec.type,
|
|
1791
|
-
initializer: spec.initializer,
|
|
1792
|
-
scope: spec.scope ? SCOPE_MAP[spec.scope] : void 0,
|
|
1793
|
-
hasExclamationToken: spec.initializer ? false : !!spec.hasExclamation,
|
|
1794
|
-
decorators: [
|
|
1795
|
-
spec.bare && !spec.decoratorArgsText ? { name: spec.decoratorName } : {
|
|
1796
|
-
name: spec.decoratorName,
|
|
1797
|
-
arguments: spec.decoratorArgsText ? [spec.decoratorArgsText] : []
|
|
1798
|
-
}
|
|
1799
|
-
]
|
|
1800
|
-
});
|
|
1801
|
-
return true;
|
|
1802
|
-
}
|
|
1803
|
-
function addGetter(cls, spec) {
|
|
1804
|
-
if (cls.getGetAccessor(spec.name)) return false;
|
|
1805
|
-
const ctor = cls.getConstructors()[0];
|
|
1806
|
-
const index = ctor ? ctor.getChildIndex() + 1 : propertyInsertIndex(cls);
|
|
1807
|
-
cls.insertGetAccessor(index, {
|
|
1808
|
-
name: spec.name,
|
|
1809
|
-
returnType: spec.returnType,
|
|
1810
|
-
statements: spec.statements ?? "// TODO: derive and return the computed value"
|
|
1811
|
-
});
|
|
1812
|
-
return true;
|
|
1813
|
-
}
|
|
1814
2131
|
function buildDecoratorArgs(options) {
|
|
1815
2132
|
const top = [];
|
|
1816
2133
|
if (options.containerProperty) {
|
|
@@ -3076,6 +3393,8 @@ exports.LOCALIZED_PLUGIN_TYPES = LOCALIZED_PLUGIN_TYPES;
|
|
|
3076
3393
|
exports.PLUGIN_TYPES = PLUGIN_TYPES;
|
|
3077
3394
|
exports.PluginHandlerFactory = PluginHandlerFactory;
|
|
3078
3395
|
exports.TrackingFileSystem = TrackingFileSystem;
|
|
3396
|
+
exports.UPDATE_RELEASE_VERSION_SCRIPT = UPDATE_RELEASE_VERSION_SCRIPT;
|
|
3397
|
+
exports.UPDATE_RELEASE_VERSION_SCRIPT_PATH = UPDATE_RELEASE_VERSION_SCRIPT_PATH;
|
|
3079
3398
|
exports.ValidationError = ValidationError;
|
|
3080
3399
|
exports.addChildToModel = addChildToModel;
|
|
3081
3400
|
exports.addComputedToModel = addComputedToModel;
|
|
@@ -3083,17 +3402,23 @@ exports.addConfigPropertyToModel = addConfigPropertyToModel;
|
|
|
3083
3402
|
exports.addContainerSupportToModel = addContainerSupportToModel;
|
|
3084
3403
|
exports.addDependencyToModel = addDependencyToModel;
|
|
3085
3404
|
exports.addFutureToModel = addFutureToModel;
|
|
3405
|
+
exports.addJavaArtifactToManifests = addJavaArtifactToManifests;
|
|
3086
3406
|
exports.addKosModelConfiguration = addKosModelConfiguration;
|
|
3087
3407
|
exports.addModelEffectToModel = addModelEffectToModel;
|
|
3088
3408
|
exports.addPropertyToModel = addPropertyToModel;
|
|
3089
3409
|
exports.addServiceRequestToModel = addServiceRequestToModel;
|
|
3090
3410
|
exports.addTopicHandlerToModel = addTopicHandlerToModel;
|
|
3091
3411
|
exports.appendBarrelExport = appendBarrelExport;
|
|
3412
|
+
exports.buildKabTargets = buildKabTargets;
|
|
3092
3413
|
exports.camelCase = camelCase;
|
|
3093
3414
|
exports.constantCase = constantCase;
|
|
3094
3415
|
exports.dashCase = dashCase;
|
|
3095
3416
|
exports.describeModel = describeModel;
|
|
3417
|
+
exports.discoverJavaArtifacts = discoverJavaArtifacts;
|
|
3096
3418
|
exports.discoverProjects = discoverProjects;
|
|
3419
|
+
exports.discoverUiArtifacts = discoverUiArtifacts;
|
|
3420
|
+
exports.ensureJavaAggregatorPom = ensureJavaAggregatorPom;
|
|
3421
|
+
exports.finalizeArchetypeModule = finalizeArchetypeModule;
|
|
3097
3422
|
exports.findProjectByName = findProjectByName;
|
|
3098
3423
|
exports.findProjectForPath = findProjectForPath;
|
|
3099
3424
|
exports.formatFiles = formatFiles;
|
|
@@ -3105,6 +3430,7 @@ exports.generateFilesFromTemplates = generateFilesFromTemplates;
|
|
|
3105
3430
|
exports.generateHook = generateHook;
|
|
3106
3431
|
exports.generateInit = generateInit;
|
|
3107
3432
|
exports.generateModel = generateModel;
|
|
3433
|
+
exports.generatePolyglotWorkspace = generatePolyglotWorkspace;
|
|
3108
3434
|
exports.generateSplashProject = generateSplashProject;
|
|
3109
3435
|
exports.getCodegenLogger = getCodegenLogger;
|
|
3110
3436
|
exports.getCurrentDirectoryName = getCurrentDirectoryName;
|
|
@@ -3120,7 +3446,9 @@ exports.pascalCase = pascalCase;
|
|
|
3120
3446
|
exports.properCase = properCase;
|
|
3121
3447
|
exports.readJson = readJson;
|
|
3122
3448
|
exports.readNxJson = readNxJson;
|
|
3449
|
+
exports.resolveModelFilePath = resolveModelFilePath;
|
|
3123
3450
|
exports.setCodegenLogger = setCodegenLogger;
|
|
3451
|
+
exports.syncCiManifests = syncCiManifests;
|
|
3124
3452
|
exports.updateJson = updateJson;
|
|
3125
3453
|
exports.updateModelIndex = updateModelIndex;
|
|
3126
3454
|
exports.validateModel = validateModel;
|