@skmtc/core 0.28.7 → 0.29.0

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 (59) hide show
  1. package/esm/context/GenerateContext.js +16 -16
  2. package/esm/context/RenderContext.d.ts +7 -7
  3. package/esm/context/RenderContext.js +17 -12
  4. package/esm/deps/jsr.io/@std/path/1.1.6/posix/is_absolute.d.ts +16 -0
  5. package/esm/deps/jsr.io/@std/path/1.1.6/posix/is_absolute.js +23 -0
  6. package/esm/deps/jsr.io/@std/path/1.1.6/windows/is_absolute.d.ts +16 -0
  7. package/esm/deps/jsr.io/@std/path/1.1.6/windows/is_absolute.js +38 -0
  8. package/esm/dsl/model/ModelDriver.js +4 -3
  9. package/esm/dsl/operation/gql/GqlOperationDriver.js +4 -3
  10. package/esm/dsl/operation/oas/OasOperationDriver.js +4 -3
  11. package/esm/dsl/webhook/WebhookDriver.js +4 -3
  12. package/esm/helpers/applyGeneratedSuffix.js +1 -1
  13. package/esm/helpers/isImported.d.ts +10 -74
  14. package/esm/helpers/isImported.js +12 -76
  15. package/esm/helpers/normalizeWorkspacePath.d.ts +64 -0
  16. package/esm/helpers/normalizeWorkspacePath.js +98 -0
  17. package/esm/helpers/toResolvedArtifactPath.d.ts +8 -5
  18. package/esm/helpers/toResolvedArtifactPath.js +11 -7
  19. package/esm/helpers/toWorkspacePath.d.ts +3 -3
  20. package/esm/helpers/toWorkspacePath.js +5 -5
  21. package/esm/mod.d.ts +1 -0
  22. package/esm/mod.js +1 -0
  23. package/esm/types/Settings.js +6 -8
  24. package/package.json +1 -1
  25. package/script/context/GenerateContext.js +16 -16
  26. package/script/context/RenderContext.d.ts +7 -7
  27. package/script/context/RenderContext.js +17 -12
  28. package/script/deps/jsr.io/@std/path/1.1.6/posix/is_absolute.d.ts +16 -0
  29. package/script/deps/jsr.io/@std/path/1.1.6/posix/is_absolute.js +26 -0
  30. package/script/deps/jsr.io/@std/path/1.1.6/windows/is_absolute.d.ts +16 -0
  31. package/script/deps/jsr.io/@std/path/1.1.6/windows/is_absolute.js +41 -0
  32. package/script/dsl/model/ModelDriver.js +4 -3
  33. package/script/dsl/operation/gql/GqlOperationDriver.js +4 -3
  34. package/script/dsl/operation/oas/OasOperationDriver.js +4 -3
  35. package/script/dsl/webhook/WebhookDriver.js +4 -3
  36. package/script/helpers/applyGeneratedSuffix.js +1 -1
  37. package/script/helpers/isImported.d.ts +10 -74
  38. package/script/helpers/isImported.js +12 -76
  39. package/script/helpers/normalizeWorkspacePath.d.ts +64 -0
  40. package/script/helpers/normalizeWorkspacePath.js +107 -0
  41. package/script/helpers/toResolvedArtifactPath.d.ts +8 -5
  42. package/script/helpers/toResolvedArtifactPath.js +11 -7
  43. package/script/helpers/toWorkspacePath.d.ts +3 -3
  44. package/script/helpers/toWorkspacePath.js +5 -5
  45. package/script/mod.d.ts +1 -0
  46. package/script/mod.js +1 -0
  47. package/script/types/Settings.js +7 -9
  48. package/esm/deps/jsr.io/@std/path/1.1.6/extname.d.ts +0 -24
  49. package/esm/deps/jsr.io/@std/path/1.1.6/extname.js +0 -31
  50. package/esm/deps/jsr.io/@std/path/1.1.6/normalize.d.ts +0 -25
  51. package/esm/deps/jsr.io/@std/path/1.1.6/normalize.js +0 -32
  52. package/esm/deps/jsr.io/@std/path/1.1.6/windows/extname.d.ts +0 -17
  53. package/esm/deps/jsr.io/@std/path/1.1.6/windows/extname.js +0 -83
  54. package/script/deps/jsr.io/@std/path/1.1.6/extname.d.ts +0 -24
  55. package/script/deps/jsr.io/@std/path/1.1.6/extname.js +0 -34
  56. package/script/deps/jsr.io/@std/path/1.1.6/normalize.d.ts +0 -25
  57. package/script/deps/jsr.io/@std/path/1.1.6/normalize.js +0 -35
  58. package/script/deps/jsr.io/@std/path/1.1.6/windows/extname.d.ts +0 -17
  59. package/script/deps/jsr.io/@std/path/1.1.6/windows/extname.js +0 -86
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ // Copyright 2018-2026 the Deno authors. MIT license.
3
+ // This module is browser compatible.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.isAbsolute = isAbsolute;
6
+ const constants_js_1 = require("../_common/constants.js");
7
+ const assert_path_js_1 = require("../_common/assert_path.js");
8
+ const _util_js_1 = require("./_util.js");
9
+ /**
10
+ * Verifies whether provided path is absolute.
11
+ *
12
+ * @example Usage
13
+ * ```ts
14
+ * import { isAbsolute } from "@std/path/windows/is-absolute";
15
+ * import { assert, assertFalse } from "@std/assert";
16
+ *
17
+ * assert(isAbsolute("C:\\foo\\bar"));
18
+ * assertFalse(isAbsolute("..\\baz"));
19
+ * ```
20
+ *
21
+ * @param path The path to verify.
22
+ * @returns `true` if the path is absolute, `false` otherwise.
23
+ */
24
+ function isAbsolute(path) {
25
+ (0, assert_path_js_1.assertPath)(path);
26
+ const len = path.length;
27
+ if (len === 0)
28
+ return false;
29
+ const code = path.charCodeAt(0);
30
+ if ((0, _util_js_1.isPathSeparator)(code)) {
31
+ return true;
32
+ }
33
+ else if ((0, _util_js_1.isWindowsDeviceRoot)(code)) {
34
+ // Possible device root
35
+ if (len > 2 && path.charCodeAt(1) === constants_js_1.CHAR_COLON) {
36
+ if ((0, _util_js_1.isPathSeparator)(path.charCodeAt(2)))
37
+ return true;
38
+ }
39
+ }
40
+ return false;
41
+ }
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ModelDriver = void 0;
7
- const normalize_js_1 = require("../../deps/jsr.io/@std/path/1.1.6/normalize.js");
7
+ const normalizeWorkspacePath_js_1 = require("../../helpers/normalizeWorkspacePath.js");
8
8
  const GeneratorKeys_js_1 = require("../GeneratorKeys.js");
9
9
  const Variant_js_1 = require("../../types/Variant.js");
10
10
  const ProjectionOptions_js_1 = require("../../types/ProjectionOptions.js");
@@ -110,7 +110,8 @@ class ModelDriver {
110
110
  apply({ destinationPath } = {}) {
111
111
  const { identifier, exportPath } = this.settings;
112
112
  const definition = this.getDefinition({ identifier, exportPath });
113
- if (destinationPath && (0, normalize_js_1.normalize)(exportPath) !== (0, normalize_js_1.normalize)(destinationPath)) {
113
+ if (destinationPath &&
114
+ (0, normalizeWorkspacePath_js_1.normalizeWorkspacePath)(exportPath) !== (0, normalizeWorkspacePath_js_1.normalizeWorkspacePath)(destinationPath)) {
114
115
  // Cross-file import of the peer's identifier from its export path.
115
116
  // The language builds the import object (`toImport`) and creates the
116
117
  // destination file on first write (caller-side); the engine stores
@@ -130,7 +131,7 @@ class ModelDriver {
130
131
  * cache-hit path). Returns the normalized path.
131
132
  */
132
133
  ensureFile(path) {
133
- const normalizedPath = (0, normalize_js_1.normalize)(path);
134
+ const normalizedPath = (0, normalizeWorkspacePath_js_1.normalizeWorkspacePath)(path);
134
135
  if (!this.context.getFile(normalizedPath)) {
135
136
  this.context.addFile(this.projection.lang.createFile({ path: normalizedPath, settings: this.context.settings }));
136
137
  }
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.GqlOperationDriver = void 0;
7
- const normalize_js_1 = require("../../../deps/jsr.io/@std/path/1.1.6/normalize.js");
7
+ const normalizeWorkspacePath_js_1 = require("../../../helpers/normalizeWorkspacePath.js");
8
8
  const GeneratorKeys_js_1 = require("../../GeneratorKeys.js");
9
9
  const Variant_js_1 = require("../../../types/Variant.js");
10
10
  const ProjectionOptions_js_1 = require("../../../types/ProjectionOptions.js");
@@ -101,7 +101,8 @@ class GqlOperationDriver {
101
101
  apply({ destinationPath } = {}) {
102
102
  const { identifier, exportPath } = this.settings;
103
103
  const definition = this.getDefinition({ identifier, exportPath });
104
- if (destinationPath && (0, normalize_js_1.normalize)(exportPath) !== (0, normalize_js_1.normalize)(destinationPath)) {
104
+ if (destinationPath &&
105
+ (0, normalizeWorkspacePath_js_1.normalizeWorkspacePath)(exportPath) !== (0, normalizeWorkspacePath_js_1.normalizeWorkspacePath)(destinationPath)) {
105
106
  // Cross-file import of the peer's identifier from its export path.
106
107
  // The language builds the import object (`toImport`) and creates the
107
108
  // destination file on first write (caller-side); the engine stores
@@ -123,7 +124,7 @@ class GqlOperationDriver {
123
124
  * cache-hit path). Returns the normalized path.
124
125
  */
125
126
  ensureFile(path) {
126
- const normalizedPath = (0, normalize_js_1.normalize)(path);
127
+ const normalizedPath = (0, normalizeWorkspacePath_js_1.normalizeWorkspacePath)(path);
127
128
  if (!this.context.getFile(normalizedPath)) {
128
129
  this.context.addFile(this.projection.lang.createFile({ path: normalizedPath, settings: this.context.settings }));
129
130
  }
@@ -16,7 +16,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
16
16
  var _OasOperationDriver_into;
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.OasOperationDriver = void 0;
19
- const normalize_js_1 = require("../../../deps/jsr.io/@std/path/1.1.6/normalize.js");
19
+ const normalizeWorkspacePath_js_1 = require("../../../helpers/normalizeWorkspacePath.js");
20
20
  const tiny_invariant_1 = __importDefault(require("tiny-invariant"));
21
21
  const GeneratorKeys_js_1 = require("../../GeneratorKeys.js");
22
22
  const Variant_js_1 = require("../../../types/Variant.js");
@@ -140,7 +140,8 @@ class OasOperationDriver {
140
140
  const { identifier, exportPath } = this.settings;
141
141
  // The file an import has to be stitched into, or `undefined` when there
142
142
  // is none to stitch — no destination, or the projection's own file.
143
- const importInto = destinationPath && (0, normalize_js_1.normalize)(exportPath) !== (0, normalize_js_1.normalize)(destinationPath)
143
+ const importInto = destinationPath &&
144
+ (0, normalizeWorkspacePath_js_1.normalizeWorkspacePath)(exportPath) !== (0, normalizeWorkspacePath_js_1.normalizeWorkspacePath)(destinationPath)
144
145
  ? destinationPath
145
146
  : undefined;
146
147
  // A member is declared inside its container, so it has no module-level
@@ -174,7 +175,7 @@ class OasOperationDriver {
174
175
  * cache-hit path). Returns the normalized path.
175
176
  */
176
177
  ensureFile(path) {
177
- const normalizedPath = (0, normalize_js_1.normalize)(path);
178
+ const normalizedPath = (0, normalizeWorkspacePath_js_1.normalizeWorkspacePath)(path);
178
179
  if (!this.context.getFile(normalizedPath)) {
179
180
  this.context.addFile(this.projection.lang.createFile({ path: normalizedPath, settings: this.context.settings }));
180
181
  }
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.WebhookDriver = void 0;
7
- const normalize_js_1 = require("../../deps/jsr.io/@std/path/1.1.6/normalize.js");
7
+ const normalizeWorkspacePath_js_1 = require("../../helpers/normalizeWorkspacePath.js");
8
8
  const GeneratorKeys_js_1 = require("../GeneratorKeys.js");
9
9
  const Variant_js_1 = require("../../types/Variant.js");
10
10
  const ProjectionOptions_js_1 = require("../../types/ProjectionOptions.js");
@@ -101,7 +101,8 @@ class WebhookDriver {
101
101
  apply({ destinationPath } = {}) {
102
102
  const { identifier, exportPath } = this.settings;
103
103
  const definition = this.getDefinition({ identifier, exportPath });
104
- if (destinationPath && (0, normalize_js_1.normalize)(exportPath) !== (0, normalize_js_1.normalize)(destinationPath)) {
104
+ if (destinationPath &&
105
+ (0, normalizeWorkspacePath_js_1.normalizeWorkspacePath)(exportPath) !== (0, normalizeWorkspacePath_js_1.normalizeWorkspacePath)(destinationPath)) {
105
106
  // Cross-file import of the peer's identifier from its export path.
106
107
  this.ensureFile(destinationPath);
107
108
  this.context.register({
@@ -116,7 +117,7 @@ class WebhookDriver {
116
117
  * projection's language. Returns the normalized path.
117
118
  */
118
119
  ensureFile(path) {
119
- const normalizedPath = (0, normalize_js_1.normalize)(path);
120
+ const normalizedPath = (0, normalizeWorkspacePath_js_1.normalizeWorkspacePath)(path);
120
121
  if (!this.context.getFile(normalizedPath)) {
121
122
  this.context.addFile(this.projection.lang.createFile({ path: normalizedPath, settings: this.context.settings }));
122
123
  }
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.removeGeneratedSuffix = exports.applyGeneratedSuffix = exports.DEFAULT_GENERATED_SUFFIX = void 0;
4
- const extname_js_1 = require("../deps/jsr.io/@std/path/1.1.6/extname.js");
4
+ const extname_js_1 = require("../deps/jsr.io/@std/path/1.1.6/posix/extname.js");
5
5
  /**
6
6
  * The filename suffix marking a file as engine-owned, applied by the
7
7
  * engine when a projection's `toExportPath` result is stored into
@@ -1,81 +1,17 @@
1
1
  /**
2
- * Determines whether two file paths represent different locations (import required).
2
+ * Whether two workspace paths name different files, so a symbol from one
3
+ * must be imported into the other.
3
4
  *
4
- * This utility function normalizes two file paths and compares them to determine
5
- * if they represent different files. It's used throughout the SKMTC system to
6
- * decide when import statements are needed - if two paths are different, an
7
- * import is required to access symbols from one file in another.
5
+ * Both are canonicalized with {@link normalizeWorkspacePath}, so every
6
+ * spelling of one file compares equal: `@/`, `./`, the bare form, and
7
+ * Windows separators. Throws, as that does, for a path that cannot name a
8
+ * file below `basePath` (a `..` segment, a Windows drive path).
8
9
  *
9
- * The function handles path normalization to ensure consistent comparisons
10
- * across different operating systems and path formats (handling './' vs absolute
11
- * paths, different separators, etc.).
12
- *
13
- * @param pathOne - The first file path to compare
14
- * @param pathTwo - The second file path to compare
15
- * @returns `true` if the paths are different (import needed), `false` if they're the same file
16
- *
17
- * @example Basic path comparison
18
- * ```typescript
19
- * import { isImported } from '@skmtc/core';
20
- *
21
- * // Same file, no import needed
22
- * console.log(isImported('./src/user.ts', './src/user.ts')); // false
23
- *
24
- * // Different files, import needed
25
- * console.log(isImported('./src/user.ts', './src/product.ts')); // true
26
- * ```
27
- *
28
- * @example Path normalization handling
29
- * ```typescript
30
- * // These all represent the same file after normalization
31
- * console.log(isImported('./src/user.ts', 'src/user.ts')); // false
32
- * console.log(isImported('src/./user.ts', 'src/user.ts')); // false
33
- * console.log(isImported('src/models/../user.ts', 'src/user.ts')); // false
34
- * ```
35
- *
36
- * @example Usage in import decision logic
10
+ * @example
37
11
  * ```typescript
38
- * class FileGenerator {
39
- * addImportIfNeeded(
40
- * sourceFile: string,
41
- * targetFile: string,
42
- * importName: string
43
- * ) {
44
- * if (isImported(sourceFile, targetFile)) {
45
- * // Files are different, add import
46
- * this.addImport(sourceFile, targetFile, importName);
47
- * console.log(`Added import: ${importName} from ${targetFile}`);
48
- * } else {
49
- * // Same file, no import needed
50
- * console.log(`No import needed - same file: ${sourceFile}`);
51
- * }
52
- * }
53
- * }
54
- * ```
55
- *
56
- * @example Cross-platform path handling
57
- * ```typescript
58
- * // Works consistently across platforms
59
- * const windowsPath = 'src\\models\\user.ts';
60
- * const unixPath = 'src/models/user.ts';
61
- *
62
- * console.log(isImported(windowsPath, unixPath)); // false (same file)
63
- * ```
64
- *
65
- * @example In generator context
66
- * ```typescript
67
- * class ModelGenerator {
68
- * generateModel(modelPath: string, referencePath: string) {
69
- * const definition = this.createModelDefinition();
70
- *
71
- * if (isImported(modelPath, referencePath)) {
72
- * // Need to import the referenced model
73
- * definition.addImport(referencePath, 'ReferencedModel');
74
- * }
75
- *
76
- * return definition;
77
- * }
78
- * }
12
+ * isImported('@/src/user.ts', './src/user.ts') // false — one file
13
+ * isImported('src\\user.ts', 'src/user.ts') // false — one file
14
+ * isImported('@/src/user.ts', '@/src/product.ts') // true
79
15
  * ```
80
16
  */
81
17
  export declare const isImported: (pathOne: string, pathTwo: string) => boolean;
@@ -1,88 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isImported = void 0;
4
- const normalize_js_1 = require("../deps/jsr.io/@std/path/1.1.6/normalize.js");
4
+ const normalizeWorkspacePath_js_1 = require("./normalizeWorkspacePath.js");
5
5
  /**
6
- * Determines whether two file paths represent different locations (import required).
6
+ * Whether two workspace paths name different files, so a symbol from one
7
+ * must be imported into the other.
7
8
  *
8
- * This utility function normalizes two file paths and compares them to determine
9
- * if they represent different files. It's used throughout the SKMTC system to
10
- * decide when import statements are needed - if two paths are different, an
11
- * import is required to access symbols from one file in another.
9
+ * Both are canonicalized with {@link normalizeWorkspacePath}, so every
10
+ * spelling of one file compares equal: `@/`, `./`, the bare form, and
11
+ * Windows separators. Throws, as that does, for a path that cannot name a
12
+ * file below `basePath` (a `..` segment, a Windows drive path).
12
13
  *
13
- * The function handles path normalization to ensure consistent comparisons
14
- * across different operating systems and path formats (handling './' vs absolute
15
- * paths, different separators, etc.).
16
- *
17
- * @param pathOne - The first file path to compare
18
- * @param pathTwo - The second file path to compare
19
- * @returns `true` if the paths are different (import needed), `false` if they're the same file
20
- *
21
- * @example Basic path comparison
22
- * ```typescript
23
- * import { isImported } from '@skmtc/core';
24
- *
25
- * // Same file, no import needed
26
- * console.log(isImported('./src/user.ts', './src/user.ts')); // false
27
- *
28
- * // Different files, import needed
29
- * console.log(isImported('./src/user.ts', './src/product.ts')); // true
30
- * ```
31
- *
32
- * @example Path normalization handling
33
- * ```typescript
34
- * // These all represent the same file after normalization
35
- * console.log(isImported('./src/user.ts', 'src/user.ts')); // false
36
- * console.log(isImported('src/./user.ts', 'src/user.ts')); // false
37
- * console.log(isImported('src/models/../user.ts', 'src/user.ts')); // false
38
- * ```
39
- *
40
- * @example Usage in import decision logic
14
+ * @example
41
15
  * ```typescript
42
- * class FileGenerator {
43
- * addImportIfNeeded(
44
- * sourceFile: string,
45
- * targetFile: string,
46
- * importName: string
47
- * ) {
48
- * if (isImported(sourceFile, targetFile)) {
49
- * // Files are different, add import
50
- * this.addImport(sourceFile, targetFile, importName);
51
- * console.log(`Added import: ${importName} from ${targetFile}`);
52
- * } else {
53
- * // Same file, no import needed
54
- * console.log(`No import needed - same file: ${sourceFile}`);
55
- * }
56
- * }
57
- * }
58
- * ```
59
- *
60
- * @example Cross-platform path handling
61
- * ```typescript
62
- * // Works consistently across platforms
63
- * const windowsPath = 'src\\models\\user.ts';
64
- * const unixPath = 'src/models/user.ts';
65
- *
66
- * console.log(isImported(windowsPath, unixPath)); // false (same file)
67
- * ```
68
- *
69
- * @example In generator context
70
- * ```typescript
71
- * class ModelGenerator {
72
- * generateModel(modelPath: string, referencePath: string) {
73
- * const definition = this.createModelDefinition();
74
- *
75
- * if (isImported(modelPath, referencePath)) {
76
- * // Need to import the referenced model
77
- * definition.addImport(referencePath, 'ReferencedModel');
78
- * }
79
- *
80
- * return definition;
81
- * }
82
- * }
16
+ * isImported('@/src/user.ts', './src/user.ts') // false — one file
17
+ * isImported('src\\user.ts', 'src/user.ts') // false — one file
18
+ * isImported('@/src/user.ts', '@/src/product.ts') // true
83
19
  * ```
84
20
  */
85
21
  const isImported = (pathOne, pathTwo) => {
86
- return (0, normalize_js_1.normalize)(pathOne) !== (0, normalize_js_1.normalize)(pathTwo);
22
+ return (0, normalizeWorkspacePath_js_1.normalizeWorkspacePath)(pathOne) !== (0, normalizeWorkspacePath_js_1.normalizeWorkspacePath)(pathTwo);
87
23
  };
88
24
  exports.isImported = isImported;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Options for {@link normalizeWorkspacePath} and {@link toNormalizedWorkspacePath}.
3
+ */
4
+ export type NormalizeWorkspacePathOptions = {
5
+ /** Names the generator in the error when the path is rejected. */
6
+ generatorId?: string;
7
+ };
8
+ /**
9
+ * Whether a path contains a `..` parent-reference segment. `..` is a parent
10
+ * reference only as a whole segment — a directory name that merely contains
11
+ * dots is not flagged.
12
+ */
13
+ export declare const hasParentSegment: (path: string) => boolean;
14
+ /**
15
+ * Whether a path is absolute on either host: a POSIX root, a Windows drive
16
+ * (`C:\`, `C:/`) or a UNC share. Checked on every host, so a client.json
17
+ * written on Windows is rejected the same way on Linux.
18
+ */
19
+ export declare const isAbsolutePath: (path: string) => boolean;
20
+ /**
21
+ * Whether `path` is spelled from the workspace root — `@/`, `./`, a bare
22
+ * leading `/`, or their Windows forms. An artifact path, never a bare
23
+ * module specifier such as `zod` or `@tanstack/query`.
24
+ */
25
+ export declare const hasWorkspaceAnchor: (path: string) => boolean;
26
+ /**
27
+ * Whether `path` can name a file below `basePath`: not a Windows drive or
28
+ * UNC path, no `..` segment, and not `basePath` itself. The non-throwing
29
+ * form of {@link normalizeWorkspacePath}'s checks, for schema validation
30
+ * and for comparing a module specifier against a file path.
31
+ */
32
+ export declare const isValidWorkspacePath: (path: string) => boolean;
33
+ /**
34
+ * The path relative to `basePath`, normalized and with no anchor:
35
+ * what {@link normalizeWorkspacePath} writes after `@/`, and what
36
+ * {@link toResolvedArtifactPath} joins onto `basePath`.
37
+ *
38
+ * Throws when the path cannot name a file below `basePath`: a Windows
39
+ * drive or UNC path, a `..` segment (checked as a whole segment before
40
+ * normalization, so it is never resolved), or a path naming `basePath`
41
+ * itself (`@/`, `@`, `./`, `.`, `/`, ``).
42
+ */
43
+ export declare const toNormalizedWorkspacePath: (path: string, { generatorId }?: NormalizeWorkspacePathOptions) => string;
44
+ /**
45
+ * The one spelling of a workspace path — an export path or a destination
46
+ * path: `@/` followed by a POSIX path relative to `basePath`.
47
+ *
48
+ * A workspace path is a logical path inside the workspace, never a
49
+ * filesystem path, so it reads the same on every host. A generator may
50
+ * return one as `@/types/User.ts`, `./types/User.ts`, `/types/User.ts`,
51
+ * `types/User.ts`, or with Windows separators; the engine stores this form
52
+ * and nothing else, so file-map keys, import modules and the ejected lookup
53
+ * always agree. The result is a fixed point.
54
+ *
55
+ * Throws in the cases {@link toNormalizedWorkspacePath} throws.
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * normalizeWorkspacePath('@\\types\\User.ts') // '@/types/User.ts'
60
+ * normalizeWorkspacePath('./types/User.ts') // '@/types/User.ts'
61
+ * normalizeWorkspacePath('../User.ts') // throws
62
+ * ```
63
+ */
64
+ export declare const normalizeWorkspacePath: (path: string, options?: NormalizeWorkspacePathOptions) => string;
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeWorkspacePath = exports.toNormalizedWorkspacePath = exports.isValidWorkspacePath = exports.hasWorkspaceAnchor = exports.isAbsolutePath = exports.hasParentSegment = void 0;
4
+ const normalize_js_1 = require("../deps/jsr.io/@std/path/1.1.6/posix/normalize.js");
5
+ const is_absolute_js_1 = require("../deps/jsr.io/@std/path/1.1.6/posix/is_absolute.js");
6
+ const is_absolute_js_2 = require("../deps/jsr.io/@std/path/1.1.6/windows/is_absolute.js");
7
+ const toWorkspacePath_js_1 = require("./toWorkspacePath.js");
8
+ /**
9
+ * Whether a path contains a `..` parent-reference segment. `..` is a parent
10
+ * reference only as a whole segment — a directory name that merely contains
11
+ * dots is not flagged.
12
+ */
13
+ const hasParentSegment = (path) => path.split(/[/\\]/).some(segment => segment === '..');
14
+ exports.hasParentSegment = hasParentSegment;
15
+ /**
16
+ * Whether a path is absolute on either host: a POSIX root, a Windows drive
17
+ * (`C:\`, `C:/`) or a UNC share. Checked on every host, so a client.json
18
+ * written on Windows is rejected the same way on Linux.
19
+ */
20
+ const isAbsolutePath = (path) => (0, is_absolute_js_1.isAbsolute)(path.replaceAll('\\', '/')) || (0, is_absolute_js_2.isAbsolute)(path);
21
+ exports.isAbsolutePath = isAbsolutePath;
22
+ /**
23
+ * A Windows drive path (`C:\`, `C:/`) or a UNC path (`\\server\share`):
24
+ * the two absolute forms that can never be relative to `basePath`. A bare
25
+ * leading `/` is not one — it is an anchor spelling of the workspace root.
26
+ */
27
+ const isDriveOrUncPath = (path) => /^[A-Za-z]:[\\/]/.test(path) || path.startsWith('\\\\');
28
+ /**
29
+ * Whether `path` is spelled from the workspace root — `@/`, `./`, a bare
30
+ * leading `/`, or their Windows forms. An artifact path, never a bare
31
+ * module specifier such as `zod` or `@tanstack/query`.
32
+ */
33
+ const hasWorkspaceAnchor = (path) => /^(@[\\/]|\.[\\/]|[\\/])/.test(path);
34
+ exports.hasWorkspaceAnchor = hasWorkspaceAnchor;
35
+ /**
36
+ * Whether `path` can name a file below `basePath`: not a Windows drive or
37
+ * UNC path, no `..` segment, and not `basePath` itself. The non-throwing
38
+ * form of {@link normalizeWorkspacePath}'s checks, for schema validation
39
+ * and for comparing a module specifier against a file path.
40
+ */
41
+ const isValidWorkspacePath = (path) => !isDriveOrUncPath(path) && !(0, exports.hasParentSegment)(path) && toBody(path) !== '';
42
+ exports.isValidWorkspacePath = isValidWorkspacePath;
43
+ /**
44
+ * The path relative to `basePath`, normalized and with no anchor:
45
+ * what {@link normalizeWorkspacePath} writes after `@/`, and what
46
+ * {@link toResolvedArtifactPath} joins onto `basePath`.
47
+ *
48
+ * Throws when the path cannot name a file below `basePath`: a Windows
49
+ * drive or UNC path, a `..` segment (checked as a whole segment before
50
+ * normalization, so it is never resolved), or a path naming `basePath`
51
+ * itself (`@/`, `@`, `./`, `.`, `/`, ``).
52
+ */
53
+ const toNormalizedWorkspacePath = (path, { generatorId } = {}) => {
54
+ const reject = (reason) => {
55
+ const source = generatorId ? ` returned by generator '${generatorId}'` : '';
56
+ throw new Error(`Path '${path}'${source} ${reason}`);
57
+ };
58
+ if (isDriveOrUncPath(path)) {
59
+ return reject('is a Windows drive or UNC path; a workspace path is relative to basePath');
60
+ }
61
+ if ((0, exports.hasParentSegment)(path)) {
62
+ return reject('contains a ".." segment; a workspace path is a forward path below basePath');
63
+ }
64
+ const body = toBody(path);
65
+ if (body === '') {
66
+ return reject('names basePath itself; a workspace path names a file below it');
67
+ }
68
+ return body;
69
+ };
70
+ exports.toNormalizedWorkspacePath = toNormalizedWorkspacePath;
71
+ /**
72
+ * The one spelling of a workspace path — an export path or a destination
73
+ * path: `@/` followed by a POSIX path relative to `basePath`.
74
+ *
75
+ * A workspace path is a logical path inside the workspace, never a
76
+ * filesystem path, so it reads the same on every host. A generator may
77
+ * return one as `@/types/User.ts`, `./types/User.ts`, `/types/User.ts`,
78
+ * `types/User.ts`, or with Windows separators; the engine stores this form
79
+ * and nothing else, so file-map keys, import modules and the ejected lookup
80
+ * always agree. The result is a fixed point.
81
+ *
82
+ * Throws in the cases {@link toNormalizedWorkspacePath} throws.
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * normalizeWorkspacePath('@\\types\\User.ts') // '@/types/User.ts'
87
+ * normalizeWorkspacePath('./types/User.ts') // '@/types/User.ts'
88
+ * normalizeWorkspacePath('../User.ts') // throws
89
+ * ```
90
+ */
91
+ const normalizeWorkspacePath = (path, options = {}) => `@/${(0, exports.toNormalizedWorkspacePath)(path, options)}`;
92
+ exports.normalizeWorkspacePath = normalizeWorkspacePath;
93
+ /**
94
+ * Separator swap, then every anchor spelling goes (`@/`, `./`, a leading
95
+ * `/` — {@link toWorkspacePath}), then POSIX normalize of what is left so
96
+ * `@//x` and `@/./x` collapse. The anchor goes first so normalize can
97
+ * never fold it into a name (`@/.` is the root, not a file called `@`).
98
+ * The root in any spelling is the empty string.
99
+ */
100
+ const toBody = (path) => {
101
+ const stripped = (0, toWorkspacePath_js_1.toWorkspacePath)(path.replaceAll('\\', '/'));
102
+ if (stripped === '') {
103
+ return '';
104
+ }
105
+ const normalized = (0, normalize_js_1.normalize)(stripped);
106
+ return normalized === '.' ? '' : normalized;
107
+ };
@@ -2,9 +2,11 @@
2
2
  * Resolves an artifact's destination path onto `basePath`.
3
3
  *
4
4
  * `destinationPath` is a workspace path in any spelling
5
- * ({@link toWorkspacePath}) — `@/models/User.ts` as a generator wrote it,
6
- * or `./models/User.ts` — and is always joined onto `basePath`; there is no
7
- * absolute-path escape.
5
+ * ({@link toNormalizedWorkspacePath}) — `@/models/User.ts` as a generator
6
+ * wrote it, `./models/User.ts`, or with Windows separators — and is always
7
+ * joined onto `basePath` with forward slashes, so the artifact key reads
8
+ * the same on every host. A `..` segment or an absolute path is refused:
9
+ * every artifact lands below `basePath`, whoever writes it.
8
10
  *
9
11
  * @module toResolvedArtifactPath
10
12
  */
@@ -22,8 +24,9 @@ type ToResolvedArtifactPathArgs = {
22
24
  destinationPath: string;
23
25
  };
24
26
  /**
25
- * Joins `destinationPath` (canonicalized) onto `basePath`, or onto `./` when
26
- * `basePath` is undefined.
27
+ * Joins `destinationPath` (canonicalized) onto `basePath`, or onto the
28
+ * workspace root when `basePath` is undefined. Throws when
29
+ * `destinationPath` cannot name a file below `basePath`.
27
30
  *
28
31
  * @example
29
32
  * ```typescript
@@ -5,18 +5,22 @@ exports.toResolvedArtifactPath = void 0;
5
5
  * Resolves an artifact's destination path onto `basePath`.
6
6
  *
7
7
  * `destinationPath` is a workspace path in any spelling
8
- * ({@link toWorkspacePath}) — `@/models/User.ts` as a generator wrote it,
9
- * or `./models/User.ts` — and is always joined onto `basePath`; there is no
10
- * absolute-path escape.
8
+ * ({@link toNormalizedWorkspacePath}) — `@/models/User.ts` as a generator
9
+ * wrote it, `./models/User.ts`, or with Windows separators — and is always
10
+ * joined onto `basePath` with forward slashes, so the artifact key reads
11
+ * the same on every host. A `..` segment or an absolute path is refused:
12
+ * every artifact lands below `basePath`, whoever writes it.
11
13
  *
12
14
  * @module toResolvedArtifactPath
13
15
  */
14
16
  require("../_dnt.polyfills.js");
15
- const join_js_1 = require("../deps/jsr.io/@std/path/1.1.6/join.js");
17
+ const join_js_1 = require("../deps/jsr.io/@std/path/1.1.6/posix/join.js");
18
+ const normalizeWorkspacePath_js_1 = require("./normalizeWorkspacePath.js");
16
19
  const toWorkspacePath_js_1 = require("./toWorkspacePath.js");
17
20
  /**
18
- * Joins `destinationPath` (canonicalized) onto `basePath`, or onto `./` when
19
- * `basePath` is undefined.
21
+ * Joins `destinationPath` (canonicalized) onto `basePath`, or onto the
22
+ * workspace root when `basePath` is undefined. Throws when
23
+ * `destinationPath` cannot name a file below `basePath`.
20
24
  *
21
25
  * @example
22
26
  * ```typescript
@@ -27,6 +31,6 @@ const toWorkspacePath_js_1 = require("./toWorkspacePath.js");
27
31
  * ```
28
32
  */
29
33
  const toResolvedArtifactPath = ({ basePath, destinationPath }) => {
30
- return (0, join_js_1.join)(basePath ?? './', (0, toWorkspacePath_js_1.toWorkspacePath)(destinationPath));
34
+ return (0, join_js_1.join)((0, toWorkspacePath_js_1.toWorkspacePath)(basePath ?? ''), (0, normalizeWorkspacePath_js_1.toNormalizedWorkspacePath)(destinationPath));
31
35
  };
32
36
  exports.toResolvedArtifactPath = toResolvedArtifactPath;
@@ -7,9 +7,9 @@
7
7
  * config and examples write it as `./`, and std `normalize` — applied to
8
8
  * `File.path` by the drivers — strips `./` but leaves `@/` alone. Anything
9
9
  * that compares paths from more than one of those sources (package matching,
10
- * on-disk resolution) must see all spellings as equal: every leading `@/` or
11
- * `./` goes, a trailing `/` goes, a Windows `\` is a `/`, and the workspace
12
- * root itself (`@/`, `./`, `.`, ``) is the empty string. The result is a fixed
10
+ * on-disk resolution) must see all spellings as equal: every leading `@/`,
11
+ * `./` or bare `/` goes, a trailing `/` goes, a Windows `\` is a `/`, and the
12
+ * workspace root itself (`@/`, `@`, `./`, `.`, `/`, ``) is the empty string. The result is a fixed
13
13
  * point: canonicalizing it again changes nothing.
14
14
  *
15
15
  * The `@/` stripped here is Skmtc's **workspace root** — the prefix
@@ -10,9 +10,9 @@ exports.isUnderRoot = exports.toWorkspacePath = void 0;
10
10
  * config and examples write it as `./`, and std `normalize` — applied to
11
11
  * `File.path` by the drivers — strips `./` but leaves `@/` alone. Anything
12
12
  * that compares paths from more than one of those sources (package matching,
13
- * on-disk resolution) must see all spellings as equal: every leading `@/` or
14
- * `./` goes, a trailing `/` goes, a Windows `\` is a `/`, and the workspace
15
- * root itself (`@/`, `./`, `.`, ``) is the empty string. The result is a fixed
13
+ * on-disk resolution) must see all spellings as equal: every leading `@/`,
14
+ * `./` or bare `/` goes, a trailing `/` goes, a Windows `\` is a `/`, and the
15
+ * workspace root itself (`@/`, `@`, `./`, `.`, `/`, ``) is the empty string. The result is a fixed
16
16
  * point: canonicalizing it again changes nothing.
17
17
  *
18
18
  * The `@/` stripped here is Skmtc's **workspace root** — the prefix
@@ -32,9 +32,9 @@ exports.isUnderRoot = exports.toWorkspacePath = void 0;
32
32
  const toWorkspacePath = (path) => {
33
33
  const withoutAnchor = path
34
34
  .replaceAll('\\', '/')
35
- .replace(/^(?:@\/|\.\/)+/, '')
35
+ .replace(/^(?:@\/|\.\/|\/)+/, '')
36
36
  .replace(/\/+$/, '');
37
- return withoutAnchor === '.' ? '' : withoutAnchor;
37
+ return withoutAnchor === '.' || withoutAnchor === '@' ? '' : withoutAnchor;
38
38
  };
39
39
  exports.toWorkspacePath = toWorkspacePath;
40
40
  /**
package/script/mod.d.ts CHANGED
@@ -118,6 +118,7 @@ export * from './helpers/strings.js';
118
118
  export * from './helpers/parseModuleName.js';
119
119
  export * from './helpers/toResolvedArtifactPath.js';
120
120
  export * from './helpers/toWorkspacePath.js';
121
+ export * from './helpers/normalizeWorkspacePath.js';
121
122
  export * from './helpers/matchPackage.js';
122
123
  export * from './helpers/withVariant.js';
123
124
  export * from './helpers/toVariantList.js';