skir 0.0.6 → 0.0.7

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.
Files changed (49) hide show
  1. package/dist/command_line_parser.d.ts.map +1 -1
  2. package/dist/command_line_parser.js +76 -36
  3. package/dist/command_line_parser.js.map +1 -1
  4. package/dist/command_line_parser.test.js +119 -31
  5. package/dist/command_line_parser.test.js.map +1 -1
  6. package/dist/compiler.js +45 -51
  7. package/dist/compiler.js.map +1 -1
  8. package/dist/config.d.ts +2 -2
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js +3 -5
  11. package/dist/config.js.map +1 -1
  12. package/dist/config_parser.d.ts +6 -0
  13. package/dist/config_parser.d.ts.map +1 -1
  14. package/dist/config_parser.js +52 -31
  15. package/dist/config_parser.js.map +1 -1
  16. package/dist/config_parser.test.js +53 -20
  17. package/dist/config_parser.test.js.map +1 -1
  18. package/dist/error_renderer.d.ts +1 -0
  19. package/dist/error_renderer.d.ts.map +1 -1
  20. package/dist/error_renderer.js +6 -3
  21. package/dist/error_renderer.js.map +1 -1
  22. package/dist/exit_error.d.ts +8 -0
  23. package/dist/exit_error.d.ts.map +1 -0
  24. package/dist/exit_error.js +8 -0
  25. package/dist/exit_error.js.map +1 -0
  26. package/dist/io.d.ts +2 -0
  27. package/dist/io.d.ts.map +1 -1
  28. package/dist/io.js +22 -3
  29. package/dist/io.js.map +1 -1
  30. package/dist/module_collector.d.ts.map +1 -1
  31. package/dist/module_collector.js +12 -7
  32. package/dist/module_collector.js.map +1 -1
  33. package/dist/module_set.js +4 -4
  34. package/dist/parser.js +6 -6
  35. package/dist/project_initializer.d.ts.map +1 -1
  36. package/dist/project_initializer.js +97 -15
  37. package/dist/project_initializer.js.map +1 -1
  38. package/package.json +8 -6
  39. package/src/command_line_parser.ts +95 -40
  40. package/src/compiler.ts +59 -57
  41. package/src/config.ts +4 -6
  42. package/src/config_parser.ts +61 -32
  43. package/src/error_renderer.ts +11 -3
  44. package/src/exit_error.ts +6 -0
  45. package/src/io.ts +22 -3
  46. package/src/module_collector.ts +21 -7
  47. package/src/module_set.ts +4 -4
  48. package/src/parser.ts +6 -6
  49. package/src/project_initializer.ts +97 -15
package/src/config.ts CHANGED
@@ -2,13 +2,11 @@ import { z } from "zod";
2
2
 
3
3
  export const GeneratorConfig = z.strictObject({
4
4
  mod: z.string(),
5
+ outDir: z.union([
6
+ z.string().endsWith("/skirout"),
7
+ z.array(z.string().endsWith("/skirout")),
8
+ ]),
5
9
  config: z.any(),
6
- outDir: z
7
- .union([
8
- z.string().endsWith("/skirout"),
9
- z.array(z.string().endsWith("/skirout")),
10
- ])
11
- .optional(),
12
10
  });
13
11
 
14
12
  export type GeneratorConfig = z.infer<typeof GeneratorConfig>;
@@ -1,16 +1,22 @@
1
- import * as ccGen from "skir-cc-gen";
2
- import * as dartGen from "skir-dart-gen";
1
+ import * as CcGen from "skir-cc-gen";
2
+ import * as DartGen from "skir-dart-gen";
3
3
  import { CodeGenerator } from "skir-internal";
4
- import * as javaGen from "skir-java-gen";
5
- import * as kotlinGen from "skir-kotlin-gen";
6
- import * as pythonGen from "skir-python-gen";
7
- import * as typescriptGen from "skir-typescript-gen";
4
+ import * as JavaGen from "skir-java-gen";
5
+ import * as KotlinGen from "skir-kotlin-gen";
6
+ import * as PythonGen from "skir-python-gen";
7
+ import * as TypescriptGen from "skir-typescript-gen";
8
8
  import { LineCounter, parseDocument, Scalar, YAMLMap } from "yaml";
9
9
  import { SkirConfig } from "./config.js";
10
10
 
11
11
  export interface SkirConfigResult {
12
+ /** Defined if and only if `errors` is empty. */
12
13
  skirConfig: SkirConfig | undefined;
13
14
  errors: readonly SkirConfigError[];
15
+ /**
16
+ * If true, the user may have forgotten to edit skir.yml after running
17
+ * `npx skir init`.
18
+ */
19
+ maybeForgotToEditAfterInit?: boolean;
14
20
  }
15
21
 
16
22
  export interface SkirConfigError {
@@ -66,6 +72,34 @@ export async function parseSkirConfig(
66
72
  }
67
73
  return offsetRangeToRange(node.range[0], node.range[1]);
68
74
  };
75
+ const pushErrorAtPath = (
76
+ path: readonly PropertyKey[],
77
+ message: string,
78
+ ): void => {
79
+ const pathRemainder: PropertyKey[] = [];
80
+ while (path.length !== 0) {
81
+ const range = pathToRange(path);
82
+ if (range) {
83
+ break;
84
+ } else {
85
+ // It's possible that 'path' does not map to a node if 'path' refers to
86
+ // a property which is missing. In that case, we pop the last element
87
+ // of 'path' and try again, until we find a node that exists. The
88
+ // elements which were popped will be included in the error message.
89
+ pathRemainder.push(path.at(-1)!);
90
+ path = path.slice(0, -1);
91
+ }
92
+ }
93
+ pathRemainder.reverse();
94
+ const messagePrefix = pathRemainder.length
95
+ ? `Missing property '${pathRemainder.join(".")}': `
96
+ : "";
97
+ const range = pathToRange(path);
98
+ errors.push({
99
+ message: messagePrefix + message,
100
+ range: range,
101
+ });
102
+ };
69
103
 
70
104
  // Check for YAML parsing errors
71
105
  if (doc.errors.length > 0) {
@@ -76,24 +110,27 @@ export async function parseSkirConfig(
76
110
  range: range,
77
111
  });
78
112
  }
79
- return { skirConfig: undefined, errors: errors };
113
+ return {
114
+ skirConfig: undefined,
115
+ errors: errors,
116
+ };
80
117
  }
81
118
 
82
- const jsData = doc.toJS();
83
-
84
119
  // 2. Validate with Zod schema
120
+ const jsData = doc.toJS();
85
121
  const result = SkirConfig.safeParse(jsData);
86
122
 
87
123
  if (!result.success) {
88
124
  for (const issue of result.error.issues) {
89
- // Map the Zod path to the YAML node
90
- const range = pathToRange(issue.path);
91
- errors.push({
92
- message: issue.message,
93
- range: range,
94
- });
125
+ pushErrorAtPath(issue.path, issue.message);
95
126
  }
96
- return { skirConfig: undefined, errors: errors };
127
+ const maybeForgotToEditAfterInit =
128
+ jsData && typeof jsData === "object" && jsData.generators === null;
129
+ return {
130
+ skirConfig: undefined,
131
+ errors: errors,
132
+ maybeForgotToEditAfterInit,
133
+ };
97
134
  }
98
135
 
99
136
  // 3. Validate each generator's config with Zod schema
@@ -106,11 +143,7 @@ export async function parseSkirConfig(
106
143
  generator = await importCodeGenerator(mod);
107
144
  } catch (e) {
108
145
  if (e instanceof Error) {
109
- const range = pathToRange(["generators", i, "mod"]);
110
- errors.push({
111
- message: e.message,
112
- range: range,
113
- });
146
+ pushErrorAtPath(["generators", i, "mod"], e.message);
114
147
  continue;
115
148
  } else {
116
149
  throw e;
@@ -119,13 +152,13 @@ export async function parseSkirConfig(
119
152
  } else {
120
153
  // TODO: rm the casts
121
154
  const modToGenerator: Record<string, CodeGenerator<unknown>> = {
122
- "skir-cc-gen": ccGen.GENERATOR as any as CodeGenerator<unknown>,
123
- "skir-dart-gen": dartGen.GENERATOR as any as CodeGenerator<unknown>,
124
- "skir-java-gen": javaGen.GENERATOR as any as CodeGenerator<unknown>,
125
- "skir-kotlin-gen": kotlinGen.GENERATOR as any as CodeGenerator<unknown>,
126
- "skir-python-gen": pythonGen.GENERATOR as any as CodeGenerator<unknown>,
155
+ "skir-cc-gen": CcGen.GENERATOR as any as CodeGenerator<unknown>,
156
+ "skir-dart-gen": DartGen.GENERATOR as any as CodeGenerator<unknown>,
157
+ "skir-java-gen": JavaGen.GENERATOR as any as CodeGenerator<unknown>,
158
+ "skir-kotlin-gen": KotlinGen.GENERATOR as any as CodeGenerator<unknown>,
159
+ "skir-python-gen": PythonGen.GENERATOR as any as CodeGenerator<unknown>,
127
160
  "skir-typescript-gen":
128
- typescriptGen.GENERATOR as any as CodeGenerator<unknown>,
161
+ TypescriptGen.GENERATOR as any as CodeGenerator<unknown>,
129
162
  };
130
163
  generator = modToGenerator[mod];
131
164
  }
@@ -141,11 +174,7 @@ export async function parseSkirConfig(
141
174
  "config",
142
175
  ...issue.path,
143
176
  ];
144
- const range = pathToRange(path);
145
- errors.push({
146
- message: issue.message ?? "Error",
147
- range: range,
148
- });
177
+ pushErrorAtPath(path, issue.message ?? "Error");
149
178
  }
150
179
  }
151
180
  }
@@ -61,6 +61,7 @@ export function renderSkirConfigErrors(
61
61
  errors: readonly SkirConfigError[],
62
62
  context: {
63
63
  skirConfigPath: string;
64
+ maybeForgotToEditAfterInit: boolean | undefined;
64
65
  },
65
66
  ): void {
66
67
  for (const error of errors) {
@@ -68,6 +69,13 @@ export function renderSkirConfigErrors(
68
69
  console.error(formatSkirConfigError(error, context));
69
70
  }
70
71
  console.error();
72
+ if (context.maybeForgotToEditAfterInit) {
73
+ const { skirConfigPath } = context;
74
+ console.warn(
75
+ `Did you forget to edit ${skirConfigPath} after running 'npx skir init'?`,
76
+ );
77
+ console.warn();
78
+ }
71
79
  }
72
80
 
73
81
  function formatSkirConfigError(
@@ -179,9 +187,9 @@ function formatBreakingChange(
179
187
  const { enumEpression, number, record, variantName } = breakingChange;
180
188
  const errorHeader = makeRed("Illegal variant kind change");
181
189
  const enumName = map(record, getQualifiedName);
182
- const variantKind = map(variantName, (vn) => {
183
- caseMatches(vn.text, "lower_underscore") ? "wrapper" : "constant";
184
- });
190
+ const variantKind = map(variantName, (vn) =>
191
+ caseMatches(vn.text, "lower_underscore") ? "wrapper" : "constant",
192
+ );
185
193
  return [
186
194
  `${locationPrefix}${errorHeader}\n`,
187
195
  " [Last snapshot]\n",
@@ -0,0 +1,6 @@
1
+ /**
2
+ * If this error is thrown during the execution of the compiler, the message
3
+ * will be printed to stderr before exiting. The stack trace will not be
4
+ * printed.
5
+ */
6
+ export class ExitError extends Error {}
package/src/io.ts CHANGED
@@ -1,4 +1,6 @@
1
- import * as fs from "fs";
1
+ import * as FileSystem from "fs";
2
+ import * as FileSystemPromises from "fs/promises";
3
+ import * as Paths from "path";
2
4
 
3
5
  export interface FileReader {
4
6
  readTextFile(path: string): string | undefined;
@@ -11,7 +13,7 @@ export interface FileWriter {
11
13
  class RealFileSystem implements FileReader, FileWriter {
12
14
  readTextFile(path: string): string | undefined {
13
15
  try {
14
- return fs.readFileSync(path, "utf-8");
16
+ return FileSystem.readFileSync(path, "utf-8");
15
17
  } catch (error) {
16
18
  if (
17
19
  error &&
@@ -26,8 +28,25 @@ class RealFileSystem implements FileReader, FileWriter {
26
28
  }
27
29
 
28
30
  writeTextFile(path: string, contents: string): void {
29
- fs.writeFileSync(path, contents, "utf-8");
31
+ FileSystem.writeFileSync(path, contents, "utf-8");
30
32
  }
31
33
  }
32
34
 
33
35
  export const REAL_FILE_SYSTEM = new RealFileSystem();
36
+
37
+ export async function isDirectory(path: string): Promise<boolean> {
38
+ try {
39
+ return (await FileSystemPromises.lstat(path)).isDirectory();
40
+ } catch (_e) {
41
+ return false;
42
+ }
43
+ }
44
+
45
+ export function rewritePathForRendering(path: string): string {
46
+ if (Paths.isAbsolute(path) || /^\.{1,2}[/\\]$/.test(path)) {
47
+ return path;
48
+ } else {
49
+ // To make it clear that it's a path, prepend "./"
50
+ return `.${Paths.sep}${path}`;
51
+ }
52
+ }
@@ -1,21 +1,35 @@
1
1
  import { glob } from "glob";
2
- import * as paths from "path";
3
- import { REAL_FILE_SYSTEM } from "./io.js";
2
+ import * as Paths from "path";
3
+ import { ExitError } from "./exit_error.js";
4
+ import {
5
+ isDirectory,
6
+ REAL_FILE_SYSTEM,
7
+ rewritePathForRendering,
8
+ } from "./io.js";
4
9
  import { ModuleSet } from "./module_set.js";
5
10
 
6
11
  export async function collectModules(srcDir: string): Promise<ModuleSet> {
7
12
  const modules = ModuleSet.create(REAL_FILE_SYSTEM, srcDir);
8
- const skirFiles = await glob(paths.join(srcDir, "**/*.skir"), {
13
+ const skirFiles = await glob(Paths.join(srcDir, "**/*.skir"), {
9
14
  stat: true,
10
15
  withFileTypes: true,
11
16
  });
12
- for await (const skirFile of skirFiles) {
17
+ if (skirFiles.length === 0) {
18
+ const isDir = await isDirectory(srcDir);
19
+ if (!isDir) {
20
+ throw new ExitError(
21
+ "Source directory does not exist: " + rewritePathForRendering(srcDir),
22
+ );
23
+ }
24
+ }
25
+ for (const skirFile of skirFiles) {
13
26
  if (!skirFile.isFile) {
14
27
  continue;
15
28
  }
16
- const relativePath = paths
17
- .relative(srcDir, skirFile.fullpath())
18
- .replace(/\\/g, "/");
29
+ const relativePath = Paths.relative(srcDir, skirFile.fullpath()).replace(
30
+ /\\/g,
31
+ "/",
32
+ );
19
33
  modules.parseAndResolve(relativePath);
20
34
  }
21
35
  return modules;
package/src/module_set.ts CHANGED
@@ -1,4 +1,4 @@
1
- import * as paths from "path";
1
+ import * as Paths from "path";
2
2
  import {
3
3
  MutableDocReferenceName,
4
4
  unquoteAndUnescape,
@@ -1311,7 +1311,7 @@ class DefaultModuleParser extends ModuleParserBase {
1311
1311
  }
1312
1312
 
1313
1313
  readSourceCode(modulePath: string): string | undefined {
1314
- return this.fileReader.readTextFile(paths.join(this.rootPath, modulePath));
1314
+ return this.fileReader.readTextFile(Paths.join(this.rootPath, modulePath));
1315
1315
  }
1316
1316
  }
1317
1317
 
@@ -1341,12 +1341,12 @@ function resolveModulePath(
1341
1341
  if (modulePath.startsWith("./") || modulePath.startsWith("../")) {
1342
1342
  // This is a relative path from the module. Let's transform it into a
1343
1343
  // relative path from root.
1344
- modulePath = paths.join(originModulePath, "..", modulePath);
1344
+ modulePath = Paths.join(originModulePath, "..", modulePath);
1345
1345
  }
1346
1346
  // "a/./b/../c" => "a/c"
1347
1347
  // Note that `paths.normalize` will use backslashes on Windows.
1348
1348
  // We don't want that.
1349
- modulePath = paths.normalize(modulePath).replace(/\\/g, "/");
1349
+ modulePath = Paths.normalize(modulePath).replace(/\\/g, "/");
1350
1350
  if (modulePath.startsWith(`../`)) {
1351
1351
  errors.push({
1352
1352
  token: pathToken,
package/src/parser.ts CHANGED
@@ -28,7 +28,7 @@ import type {
28
28
  UnresolvedType,
29
29
  } from "skir-internal";
30
30
  import { convertCase, simpleHash } from "skir-internal";
31
- import * as casing from "./casing.js";
31
+ import * as Casing from "./casing.js";
32
32
  import { mergeDocs } from "./doc_comment_parser.js";
33
33
  import { ModuleTokens } from "./tokenizer.js";
34
34
 
@@ -398,7 +398,7 @@ function parseRecord(
398
398
  if (nameMatch.case < 0) {
399
399
  return null;
400
400
  }
401
- casing.validate(nameMatch.token, "UpperCamel", it.errors);
401
+ Casing.validate(nameMatch.token, "UpperCamel", it.errors);
402
402
  nameToken = nameMatch.token;
403
403
  }
404
404
  let stableId: number | null = null;
@@ -476,7 +476,7 @@ function parseField(
476
476
  }
477
477
  case 2: {
478
478
  const expectedCasing = type ? "lower_underscore" : "UPPER_UNDERSCORE";
479
- casing.validate(name, expectedCasing, it.errors);
479
+ Casing.validate(name, expectedCasing, it.errors);
480
480
  if (recordType === "enum" && name.text === "UNKNOWN") {
481
481
  it.errors.push({
482
482
  token: name,
@@ -790,7 +790,7 @@ function parseImportAs(it: TokenIterator): ImportAlias | null {
790
790
  if (aliasMatch.case < 0) {
791
791
  return null;
792
792
  }
793
- casing.validate(aliasMatch.token, "lower_underscore", it.errors);
793
+ Casing.validate(aliasMatch.token, "lower_underscore", it.errors);
794
794
  if (it.expectThenNext(["from"]).case < 0) return null;
795
795
  const modulePathMatch = it.expectThenNext([TOKEN_IS_STRING_LITERAL]);
796
796
  if (modulePathMatch.case < 0) {
@@ -838,7 +838,7 @@ function parseMethod(it: TokenIterator, doc: Doc): MutableMethod | null {
838
838
  return null;
839
839
  }
840
840
  const name = nameMatch.token;
841
- casing.validate(name, "UpperCamel", it.errors);
841
+ Casing.validate(name, "UpperCamel", it.errors);
842
842
  if (it.expectThenNext(["("]).case < 0) {
843
843
  return null;
844
844
  }
@@ -898,7 +898,7 @@ function parseConstant(it: TokenIterator, doc: Doc): MutableConstant | null {
898
898
  if (nameMatch.case < 0) {
899
899
  return null;
900
900
  }
901
- casing.validate(nameMatch.token, "UPPER_UNDERSCORE", it.errors);
901
+ Casing.validate(nameMatch.token, "UPPER_UNDERSCORE", it.errors);
902
902
  if (it.expectThenNext([":"]).case < 0) {
903
903
  return null;
904
904
  }
@@ -1,11 +1,12 @@
1
- import * as fs from "fs";
2
- import * as paths from "path";
1
+ import * as FileSystem from "fs";
2
+ import * as Paths from "path";
3
+ import { rewritePathForRendering } from "./io.js";
3
4
 
4
5
  export function initializeProject(rootDir: string): void {
5
- const skirYmlPath = paths.join(rootDir, "skir.yml");
6
+ const skirYmlPath = Paths.join(rootDir, "skir.yml");
6
7
 
7
8
  // Check if skir.yml already exists
8
- if (fs.existsSync(skirYmlPath)) {
9
+ if (FileSystem.existsSync(skirYmlPath)) {
9
10
  console.log(
10
11
  "A skir.yml file already exists in this directory. Skipping project initialization.",
11
12
  );
@@ -13,28 +14,109 @@ export function initializeProject(rootDir: string): void {
13
14
  }
14
15
 
15
16
  // Create skir.yml file
16
- fs.writeFileSync(skirYmlPath, SKIR_YML_CONTENT, "utf-8");
17
+ FileSystem.writeFileSync(skirYmlPath, SKIR_YML_CONTENT, "utf-8");
17
18
 
18
19
  // Check if skir-src directory exists
19
- const skirSrcDir = paths.join(rootDir, "skir-src");
20
- if (!fs.existsSync(skirSrcDir)) {
20
+ const skirSrcDir = Paths.join(rootDir, "skir-src");
21
+ if (!FileSystem.existsSync(skirSrcDir)) {
21
22
  // Create skir-src directory
22
- fs.mkdirSync(skirSrcDir, { recursive: true });
23
+ FileSystem.mkdirSync(skirSrcDir, { recursive: true });
23
24
 
24
25
  // Create hello_world.skir file
25
- const helloWorldPath = paths.join(skirSrcDir, "hello_world.skir");
26
- fs.writeFileSync(helloWorldPath, HELLO_WORLD_SKIR_CONTENT, "utf-8");
26
+ const helloWorldPath = Paths.join(skirSrcDir, "hello_world.skir");
27
+ FileSystem.writeFileSync(helloWorldPath, HELLO_WORLD_SKIR_CONTENT, "utf-8");
27
28
  }
28
29
 
29
- console.log(`Done. Please edit: ${paths.resolve(skirYmlPath)}`);
30
+ console.log(`Done. Please edit: ${rewritePathForRendering(skirYmlPath)}`);
30
31
  }
31
32
 
32
- const SKIR_YML_CONTENT = `srcDir: skir-src
33
+ const SKIR_YML_CONTENT = `# Configuration file for Skir code generator
34
+ #
35
+ # Documentation: https://skir.build/
36
+ #
37
+ # Cheat sheet:
38
+ # npx skir gen Generate code from .skir files
39
+ # npx skir gen --watch Watch for changes and regenerate automatically
40
+ # npx skir format Format all .skir files
41
+ # npx skir snapshot Take a snapshot of the source directory, verify no
42
+ # breaking changes since last snapshot
33
43
 
44
+ # Directory containing .skir files
45
+ srcDir: skir-src
46
+
47
+ # Uncomment and configure the generators for your target language(s).
34
48
  generators:
35
- - mod: skir-python-gen
36
- skiroutDir: ./skirout
37
- config: {}
49
+ # # --------------------------------------------------------------------------
50
+ # # C++ code generator
51
+ # # Home: https://github.com/gepheum/skir-cc-gen
52
+ # # To install runtime dependencies, follow instructions in repository README
53
+ # # --------------------------------------------------------------------------
54
+ # - mod: skir-cc-gen
55
+ # outDir: ./skirout
56
+ # config:
57
+ # # Set to true if you use GoogleTest
58
+ # writeGoogleTestHeaders: false
59
+
60
+ # # --------------------------------------------------------------------------
61
+ # # Dart code generator
62
+ # # Home: https://github.com/gepheum/skir-dart-gen
63
+ # # To install runtime dependencies: dart pub add skir_client
64
+ # # --------------------------------------------------------------------------
65
+ # - mod: skir-dart-gen
66
+ # outDir: ./skirout
67
+ # config: {}
68
+
69
+ # # --------------------------------------------------------------------------
70
+ # # Java code generator
71
+ # # Home: https://github.com/gepheum/skir-java-gen
72
+ # # Add the following line to your build.gradle dependencies:
73
+ # # implementation("build.skir:skir-client:latest.release")
74
+ # # --------------------------------------------------------------------------
75
+ # - mod: skir-java-gen
76
+ # outDir: ./src/main/java/skirout
77
+ # config: {}
78
+ # # Alternatively:
79
+ # # outDir: ./src/main/java/skirout/my/project
80
+ # # config:
81
+ # # packagePrefix: "my.project."
82
+
83
+ # # --------------------------------------------------------------------------
84
+ # # Kotlin code generator
85
+ # # Home: https://github.com/gepheum/skir-kotlin-gen
86
+ # # Add the following line to your build.gradle dependencies:
87
+ # # implementation("build.skir:skir-client:latest.release")
88
+ # # --------------------------------------------------------------------------
89
+ # - mod: skir-kotlin-gen
90
+ # outDir: ./src/main/kotlin/skirout
91
+ # config: {}
92
+ # # Alternatively:
93
+ # # outDir: ./src/main/kotlin/skirout/my/project
94
+ # # config:
95
+ # # packagePrefix: "my.project."
96
+
97
+ # # --------------------------------------------------------------------------
98
+ # # Python code generator
99
+ # # Home: https://github.com/gepheum/skir-python-gen
100
+ # # To install runtime dependencies: pip install skir-client
101
+ # # --------------------------------------------------------------------------
102
+ # - mod: skir-python-gen
103
+ # outDir: ./skirout
104
+ # config: {}
105
+ # # Alternatively:
106
+ # # outDir: ./my/project/skirout
107
+ # # config:
108
+ # # packagePrefix: "my.project."
109
+
110
+ # # --------------------------------------------------------------------------
111
+ # # TypeScript/JavaScript code generator
112
+ # # Home: https://github.com/gepheum/skir-typescript-gen
113
+ # # To install runtime dependencies: npm i skir-client
114
+ # # --------------------------------------------------------------------------
115
+ # - mod: skir-typescript-gen
116
+ # outDir: ./src/skirout
117
+ # config:
118
+ # # Use ".js" for ES modules, "" for CommonJS
119
+ # importPathExtension: ".js"
38
120
  `;
39
121
 
40
122
  const HELLO_WORLD_SKIR_CONTENT = `/// A point in 2D space.