@defold-typescript/transpiler 0.29.0 → 0.30.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.
- package/dist/index.d.ts +3 -0
- package/dist/index.js +100 -1
- package/dist/scene-address-declaration.d.ts +12 -0
- package/dist/scene-documents.d.ts +18 -0
- package/package.json +2 -2
- package/src/index.ts +14 -0
- package/src/scene-address-declaration.ts +57 -0
- package/src/scene-documents.ts +126 -0
package/dist/index.d.ts
CHANGED
|
@@ -7,8 +7,11 @@ export type { BuildConfig, ScriptKind, SourceOutputKind } from "./output-paths";
|
|
|
7
7
|
export { computeOutputRel, DEFAULT_INCLUDE, parseBuildConfig, SCRIPT_SUFFIX_BY_KIND, stripIncludeBase, } from "./output-paths";
|
|
8
8
|
export { getProgramDiagnostics } from "./program-diagnostics";
|
|
9
9
|
export { isDefignoredPath, SCAFFOLDED_DEFIGNORE_LINES } from "./project-resources";
|
|
10
|
+
export { buildSceneAddressDeclaration } from "./scene-address-declaration";
|
|
10
11
|
export type { SceneComponentIndex } from "./scene-component-index";
|
|
11
12
|
export { buildSceneComponentIndex } from "./scene-component-index";
|
|
13
|
+
export type { SceneReadHost } from "./scene-documents";
|
|
14
|
+
export { ANIMATION_ASSET_EXTENSIONS, displayPathOf, GAME_PROJECT_DOCUMENT, GUI_EXTENSIONS, INPUT_BINDING_EXTENSIONS, isExcludedProjectPath, listProjectResourcePaths, PROJECT_EXTENSIONS, readSceneDocuments, SCENE_EXTENSIONS, } from "./scene-documents";
|
|
12
15
|
export type { SceneObjectPathIndex } from "./scene-object-path-index";
|
|
13
16
|
export { buildSceneObjectPathIndex } from "./scene-object-path-index";
|
|
14
17
|
export type { SceneMessage } from "./scene-text-format";
|
package/dist/index.js
CHANGED
|
@@ -876,6 +876,7 @@ function buildSceneComponentIndex(documents) {
|
|
|
876
876
|
}
|
|
877
877
|
return { ids, incomplete };
|
|
878
878
|
}
|
|
879
|
+
|
|
879
880
|
// src/scene-object-path-index.ts
|
|
880
881
|
var LEAF_BLOCKS = ["instances", "embedded_instances"];
|
|
881
882
|
function resourceKey(resource) {
|
|
@@ -971,6 +972,93 @@ function buildSceneObjectPathIndex(documents) {
|
|
|
971
972
|
declarers.sort();
|
|
972
973
|
return { paths, declaredIn, incomplete };
|
|
973
974
|
}
|
|
975
|
+
|
|
976
|
+
// src/scene-address-declaration.ts
|
|
977
|
+
var BANNER = `// Generated from this project's scenes by \`defold-typescript scene-types\`.
|
|
978
|
+
// Do not edit: every run rewrites it from the .go/.collection sources.
|
|
979
|
+
//
|
|
980
|
+
// Each key is an address the project declares. The aliases in
|
|
981
|
+
// \`@defold-typescript/types\` stay widened with \`(string & {})\`, so these keys
|
|
982
|
+
// only add completions — an address composed at runtime is never rejected.
|
|
983
|
+
`;
|
|
984
|
+
function componentAddressesOf(documents) {
|
|
985
|
+
return [...buildSceneComponentIndex(documents).ids].map((id) => `#${id}`);
|
|
986
|
+
}
|
|
987
|
+
function bodyOf(keys) {
|
|
988
|
+
if (keys.length === 0)
|
|
989
|
+
return "{}";
|
|
990
|
+
const members = keys.map((key) => ` ${JSON.stringify(key)}: true;
|
|
991
|
+
`).sort().join("");
|
|
992
|
+
return `{
|
|
993
|
+
${members} }`;
|
|
994
|
+
}
|
|
995
|
+
function buildSceneAddressDeclaration(documents) {
|
|
996
|
+
const paths = [...buildSceneObjectPathIndex(documents).paths];
|
|
997
|
+
const components = componentAddressesOf(documents);
|
|
998
|
+
return `${BANNER}
|
|
999
|
+
declare global {
|
|
1000
|
+
interface SceneGameObjectAddresses ${bodyOf(paths)}
|
|
1001
|
+
|
|
1002
|
+
interface SceneComponentAddresses ${bodyOf(components)}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
export {};
|
|
1006
|
+
`;
|
|
1007
|
+
}
|
|
1008
|
+
// src/scene-documents.ts
|
|
1009
|
+
var SCENE_EXTENSIONS = [".go", ".collection"];
|
|
1010
|
+
var GUI_EXTENSIONS = [".gui"];
|
|
1011
|
+
var INPUT_BINDING_EXTENSIONS = [".input_binding"];
|
|
1012
|
+
var ANIMATION_ASSET_EXTENSIONS = [".atlas", ".tilesource", ".sprite"];
|
|
1013
|
+
var PROJECT_EXTENSIONS = [".project"];
|
|
1014
|
+
var GAME_PROJECT_DOCUMENT = "game.project";
|
|
1015
|
+
function displayPathOf(projectRoot, filePath) {
|
|
1016
|
+
const path2 = filePath.replace(/\\/g, "/");
|
|
1017
|
+
const root = projectRoot.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
1018
|
+
return path2.startsWith(`${root}/`) ? path2.slice(root.length + 1) : path2;
|
|
1019
|
+
}
|
|
1020
|
+
function isBuildOutput(displayPath) {
|
|
1021
|
+
return displayPath.split("/").includes("build");
|
|
1022
|
+
}
|
|
1023
|
+
function isExcludedProjectPath(displayPath) {
|
|
1024
|
+
return isBuildOutput(displayPath) || isDefignoredPath(displayPath);
|
|
1025
|
+
}
|
|
1026
|
+
function readSceneDocuments(host, projectRoot, extensions = SCENE_EXTENSIONS) {
|
|
1027
|
+
const documents = new Map;
|
|
1028
|
+
const unreadable = [];
|
|
1029
|
+
const paths = [];
|
|
1030
|
+
if (!host.readDirectory) {
|
|
1031
|
+
unreadable.push("the editor host cannot enumerate project files, so no scene source was read");
|
|
1032
|
+
return { documents, unreadable, paths };
|
|
1033
|
+
}
|
|
1034
|
+
for (const filePath of host.readDirectory(projectRoot, extensions)) {
|
|
1035
|
+
const displayPath = displayPathOf(projectRoot, filePath);
|
|
1036
|
+
if (isExcludedProjectPath(displayPath))
|
|
1037
|
+
continue;
|
|
1038
|
+
const text = host.readFile(filePath);
|
|
1039
|
+
if (text === undefined) {
|
|
1040
|
+
unreadable.push(`${displayPath}: could not be read`);
|
|
1041
|
+
continue;
|
|
1042
|
+
}
|
|
1043
|
+
documents.set(displayPath, text);
|
|
1044
|
+
paths.push(filePath);
|
|
1045
|
+
}
|
|
1046
|
+
return { documents, unreadable, paths };
|
|
1047
|
+
}
|
|
1048
|
+
function listProjectResourcePaths(host, projectRoot, extensions) {
|
|
1049
|
+
if (!host.readDirectory)
|
|
1050
|
+
return new Set;
|
|
1051
|
+
const paths = [];
|
|
1052
|
+
for (const filePath of host.readDirectory(projectRoot, extensions)) {
|
|
1053
|
+
const displayPath = displayPathOf(projectRoot, filePath);
|
|
1054
|
+
if (isExcludedProjectPath(displayPath))
|
|
1055
|
+
continue;
|
|
1056
|
+
if (!extensions.some((extension) => displayPath.endsWith(extension)))
|
|
1057
|
+
continue;
|
|
1058
|
+
paths.push(`/${displayPath}`);
|
|
1059
|
+
}
|
|
1060
|
+
return new Set(paths.sort());
|
|
1061
|
+
}
|
|
974
1062
|
// src/session.ts
|
|
975
1063
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
976
1064
|
import { createRequire as createRequire2 } from "node:module";
|
|
@@ -1807,13 +1895,17 @@ export {
|
|
|
1807
1895
|
transpile,
|
|
1808
1896
|
stripIncludeBase,
|
|
1809
1897
|
resolveClassifiedSlotAtPosition,
|
|
1898
|
+
readSceneDocuments,
|
|
1810
1899
|
parseSceneTextFormat,
|
|
1811
1900
|
parseBuildConfig,
|
|
1901
|
+
listProjectResourcePaths,
|
|
1812
1902
|
isFragmentCaret,
|
|
1903
|
+
isExcludedProjectPath,
|
|
1813
1904
|
isDefignoredPath,
|
|
1814
1905
|
isAddressClass,
|
|
1815
1906
|
getProgramDiagnostics,
|
|
1816
1907
|
findMainEntryFactoryImports,
|
|
1908
|
+
displayPathOf,
|
|
1817
1909
|
createTranspileSession,
|
|
1818
1910
|
computeOutputRel,
|
|
1819
1911
|
componentIdOfSameObjectAddress,
|
|
@@ -1821,12 +1913,19 @@ export {
|
|
|
1821
1913
|
buildSpriteAnimationIndex,
|
|
1822
1914
|
buildSceneObjectPathIndex,
|
|
1823
1915
|
buildSceneComponentIndex,
|
|
1916
|
+
buildSceneAddressDeclaration,
|
|
1824
1917
|
buildInputActionIndex,
|
|
1825
1918
|
buildGuiNodeIndex,
|
|
1826
1919
|
buildConfigKeyIndex,
|
|
1827
1920
|
addressClassOfArgument,
|
|
1828
1921
|
SceneTextFormatError,
|
|
1829
1922
|
SCRIPT_SUFFIX_BY_KIND,
|
|
1923
|
+
SCENE_EXTENSIONS,
|
|
1830
1924
|
SCAFFOLDED_DEFIGNORE_LINES,
|
|
1831
|
-
|
|
1925
|
+
PROJECT_EXTENSIONS,
|
|
1926
|
+
INPUT_BINDING_EXTENSIONS,
|
|
1927
|
+
GUI_EXTENSIONS,
|
|
1928
|
+
GAME_PROJECT_DOCUMENT,
|
|
1929
|
+
DEFAULT_INCLUDE,
|
|
1930
|
+
ANIMATION_ASSET_EXTENSIONS
|
|
1832
1931
|
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The global augmentation that fills `SceneGameObjectAddresses` and
|
|
3
|
+
* `SceneComponentAddresses` from already-read scene sources — keys are
|
|
4
|
+
* project-relative display paths, values are file text.
|
|
5
|
+
*
|
|
6
|
+
* Pure, like the two indexes it composes: the filesystem walk belongs to the
|
|
7
|
+
* caller. Keys are sorted so a project whose scenes did not change re-emits a
|
|
8
|
+
* byte-identical file, and a document map built in a different order emits the
|
|
9
|
+
* same bytes as well; a `Set` iteration order leaking into the output would
|
|
10
|
+
* make every regeneration rewrite the file and re-check the whole program.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildSceneAddressDeclaration(documents: ReadonlyMap<string, string>): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface SceneReadHost {
|
|
2
|
+
readDirectory?(path: string, extensions?: readonly string[]): string[];
|
|
3
|
+
readFile(path: string): string | undefined;
|
|
4
|
+
}
|
|
5
|
+
export declare const SCENE_EXTENSIONS: string[];
|
|
6
|
+
export declare const GUI_EXTENSIONS: string[];
|
|
7
|
+
export declare const INPUT_BINDING_EXTENSIONS: string[];
|
|
8
|
+
export declare const ANIMATION_ASSET_EXTENSIONS: string[];
|
|
9
|
+
export declare const PROJECT_EXTENSIONS: string[];
|
|
10
|
+
export declare const GAME_PROJECT_DOCUMENT = "game.project";
|
|
11
|
+
export declare function displayPathOf(projectRoot: string, filePath: string): string;
|
|
12
|
+
export declare function isExcludedProjectPath(displayPath: string): boolean;
|
|
13
|
+
export declare function readSceneDocuments(host: SceneReadHost, projectRoot: string, extensions?: readonly string[]): {
|
|
14
|
+
documents: Map<string, string>;
|
|
15
|
+
unreadable: string[];
|
|
16
|
+
paths: string[];
|
|
17
|
+
};
|
|
18
|
+
export declare function listProjectResourcePaths(host: SceneReadHost, projectRoot: string, extensions: readonly string[]): Set<string>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defold-typescript/transpiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "TypeScript-to-Lua build pipeline tuned for Defold's runtime.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"test": "bun test"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@defold-typescript/types": "0.
|
|
38
|
+
"@defold-typescript/types": "0.30.0",
|
|
39
39
|
"@typescript-to-lua/language-extensions": "1.19.0",
|
|
40
40
|
"typescript-to-lua": "^1.36.0"
|
|
41
41
|
}
|
package/src/index.ts
CHANGED
|
@@ -13,8 +13,22 @@ export {
|
|
|
13
13
|
} from "./output-paths";
|
|
14
14
|
export { getProgramDiagnostics } from "./program-diagnostics";
|
|
15
15
|
export { isDefignoredPath, SCAFFOLDED_DEFIGNORE_LINES } from "./project-resources";
|
|
16
|
+
export { buildSceneAddressDeclaration } from "./scene-address-declaration";
|
|
16
17
|
export type { SceneComponentIndex } from "./scene-component-index";
|
|
17
18
|
export { buildSceneComponentIndex } from "./scene-component-index";
|
|
19
|
+
export type { SceneReadHost } from "./scene-documents";
|
|
20
|
+
export {
|
|
21
|
+
ANIMATION_ASSET_EXTENSIONS,
|
|
22
|
+
displayPathOf,
|
|
23
|
+
GAME_PROJECT_DOCUMENT,
|
|
24
|
+
GUI_EXTENSIONS,
|
|
25
|
+
INPUT_BINDING_EXTENSIONS,
|
|
26
|
+
isExcludedProjectPath,
|
|
27
|
+
listProjectResourcePaths,
|
|
28
|
+
PROJECT_EXTENSIONS,
|
|
29
|
+
readSceneDocuments,
|
|
30
|
+
SCENE_EXTENSIONS,
|
|
31
|
+
} from "./scene-documents";
|
|
18
32
|
export type { SceneObjectPathIndex } from "./scene-object-path-index";
|
|
19
33
|
export { buildSceneObjectPathIndex } from "./scene-object-path-index";
|
|
20
34
|
export type { SceneMessage } from "./scene-text-format";
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { buildSceneComponentIndex } from "./scene-component-index";
|
|
2
|
+
import { buildSceneObjectPathIndex } from "./scene-object-path-index";
|
|
3
|
+
|
|
4
|
+
const BANNER = `// Generated from this project's scenes by \`defold-typescript scene-types\`.
|
|
5
|
+
// Do not edit: every run rewrites it from the .go/.collection sources.
|
|
6
|
+
//
|
|
7
|
+
// Each key is an address the project declares. The aliases in
|
|
8
|
+
// \`@defold-typescript/types\` stay widened with \`(string & {})\`, so these keys
|
|
9
|
+
// only add completions — an address composed at runtime is never rejected.
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
// `buildSceneComponentIndex` reports bare component ids with no owning object,
|
|
13
|
+
// so the same-object form `#id` is the whole address the index can prove. A
|
|
14
|
+
// `/path#id` key would be a cross product of two universes that were never
|
|
15
|
+
// joined: it would claim every object owns every component.
|
|
16
|
+
function componentAddressesOf(documents: ReadonlyMap<string, string>): string[] {
|
|
17
|
+
return [...buildSceneComponentIndex(documents).ids].map((id) => `#${id}`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// An interface with no members closes on the same line: an empty body written
|
|
21
|
+
// across two lines is what a formatter would collapse, and the generated file
|
|
22
|
+
// has to survive a consumer's formatter run byte-identical or every regeneration
|
|
23
|
+
// looks like a change.
|
|
24
|
+
function bodyOf(keys: readonly string[]): string {
|
|
25
|
+
if (keys.length === 0) return "{}";
|
|
26
|
+
const members = keys
|
|
27
|
+
.map((key) => ` ${JSON.stringify(key)}: true;\n`)
|
|
28
|
+
.sort()
|
|
29
|
+
.join("");
|
|
30
|
+
return `{\n${members} }`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The global augmentation that fills `SceneGameObjectAddresses` and
|
|
35
|
+
* `SceneComponentAddresses` from already-read scene sources — keys are
|
|
36
|
+
* project-relative display paths, values are file text.
|
|
37
|
+
*
|
|
38
|
+
* Pure, like the two indexes it composes: the filesystem walk belongs to the
|
|
39
|
+
* caller. Keys are sorted so a project whose scenes did not change re-emits a
|
|
40
|
+
* byte-identical file, and a document map built in a different order emits the
|
|
41
|
+
* same bytes as well; a `Set` iteration order leaking into the output would
|
|
42
|
+
* make every regeneration rewrite the file and re-check the whole program.
|
|
43
|
+
*/
|
|
44
|
+
export function buildSceneAddressDeclaration(documents: ReadonlyMap<string, string>): string {
|
|
45
|
+
const paths = [...buildSceneObjectPathIndex(documents).paths];
|
|
46
|
+
const components = componentAddressesOf(documents);
|
|
47
|
+
|
|
48
|
+
return `${BANNER}
|
|
49
|
+
declare global {
|
|
50
|
+
interface SceneGameObjectAddresses ${bodyOf(paths)}
|
|
51
|
+
|
|
52
|
+
interface SceneComponentAddresses ${bodyOf(components)}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export {};
|
|
56
|
+
`;
|
|
57
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { isDefignoredPath } from "./project-resources";
|
|
2
|
+
|
|
3
|
+
// The narrow slice of `ts.server.ServerHost` this needs: the real host
|
|
4
|
+
// satisfies it structurally, and a test fake is two methods rather than a whole
|
|
5
|
+
// server. `readDirectory` is optional because a host may not provide it, and a
|
|
6
|
+
// plugin that throws takes the editor's completions down with it.
|
|
7
|
+
export interface SceneReadHost {
|
|
8
|
+
readDirectory?(path: string, extensions?: readonly string[]): string[];
|
|
9
|
+
readFile(path: string): string | undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const SCENE_EXTENSIONS = [".go", ".collection"];
|
|
13
|
+
|
|
14
|
+
// The extension sets more than one reader asks the host for. They live here
|
|
15
|
+
// rather than beside either caller because the index cache serves exactly the
|
|
16
|
+
// sets it is handed: a second definition that drifted would walk — and watch —
|
|
17
|
+
// a universe the other reader never invalidates.
|
|
18
|
+
export const GUI_EXTENSIONS = [".gui"];
|
|
19
|
+
export const INPUT_BINDING_EXTENSIONS = [".input_binding"];
|
|
20
|
+
|
|
21
|
+
// The documents an animation id is declared by: an atlas or tile source names
|
|
22
|
+
// the ids, and a `.sprite` is the hop a referenced component takes to reach one.
|
|
23
|
+
export const ANIMATION_ASSET_EXTENSIONS = [".atlas", ".tilesource", ".sprite"];
|
|
24
|
+
|
|
25
|
+
// The walk returns every `.project` the project holds, so the lookup is by
|
|
26
|
+
// display path — a vendored `*.project` declares keys this project's readers
|
|
27
|
+
// cannot resolve.
|
|
28
|
+
export const PROJECT_EXTENSIONS = [".project"];
|
|
29
|
+
export const GAME_PROJECT_DOCUMENT = "game.project";
|
|
30
|
+
|
|
31
|
+
// The project-relative name a file is keyed by — the same name the editor
|
|
32
|
+
// reports for a program file, so a scene's claim on a script can be looked up
|
|
33
|
+
// against the file being edited.
|
|
34
|
+
export function displayPathOf(projectRoot: string, filePath: string): string {
|
|
35
|
+
const path = filePath.replace(/\\/g, "/");
|
|
36
|
+
const root = projectRoot.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
37
|
+
return path.startsWith(`${root}/`) ? path.slice(root.length + 1) : path;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// What both walks exclude is what Defold does not load as a project resource:
|
|
41
|
+
// `build/` output, plus the directories the scaffolded `.defignore` names
|
|
42
|
+
// (`isDefignoredPath`, root-anchored because a `.defignore` line is a
|
|
43
|
+
// root-relative path).
|
|
44
|
+
//
|
|
45
|
+
// Bob writes `_generated_*.go` copies of the project's scenes under `build/`;
|
|
46
|
+
// reading them would report every id twice, against files no author edits. The
|
|
47
|
+
// segment test runs on the project-relative path, so a project that itself
|
|
48
|
+
// lives under a directory named `build` is not excluded wholesale — a different
|
|
49
|
+
// rule from `.defignore`'s, because bob writes those copies below whatever
|
|
50
|
+
// depth it is pointed at.
|
|
51
|
+
function isBuildOutput(displayPath: string): boolean {
|
|
52
|
+
return displayPath.split("/").includes("build");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// The single rule, exported because the index cache filters watch events with
|
|
56
|
+
// it: a copy in the watcher could drift from what the walks exclude, and an
|
|
57
|
+
// event the walk would have ignored is exactly the event that must not throw the
|
|
58
|
+
// cache away — bob writes `_generated_*.go` throughout a build.
|
|
59
|
+
export function isExcludedProjectPath(displayPath: string): boolean {
|
|
60
|
+
return isBuildOutput(displayPath) || isDefignoredPath(displayPath);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Read the project's own scene sources through the editor host — by default the
|
|
64
|
+
// `.go`/`.collection` set the component-id universe is built from, or whatever
|
|
65
|
+
// `extensions` names instead. The two universes stay disjoint: a `.gui` declares
|
|
66
|
+
// node ids, not component ids, so folding it into the default would feed gui
|
|
67
|
+
// text to `buildSceneComponentIndex`. Keys are project-relative display paths,
|
|
68
|
+
// matching what `buildSceneComponentIndex` reports in `incomplete`, and every
|
|
69
|
+
// file that could not be read is named in `unreadable` — a universe with a
|
|
70
|
+
// silent hole cannot be reasoned about. `paths` names the host paths actually
|
|
71
|
+
// read — what a watcher must be registered on, which cannot be reconstructed
|
|
72
|
+
// from a display path for a file outside the project root.
|
|
73
|
+
export function readSceneDocuments(
|
|
74
|
+
host: SceneReadHost,
|
|
75
|
+
projectRoot: string,
|
|
76
|
+
extensions: readonly string[] = SCENE_EXTENSIONS,
|
|
77
|
+
): { documents: Map<string, string>; unreadable: string[]; paths: string[] } {
|
|
78
|
+
const documents = new Map<string, string>();
|
|
79
|
+
const unreadable: string[] = [];
|
|
80
|
+
const paths: string[] = [];
|
|
81
|
+
|
|
82
|
+
if (!host.readDirectory) {
|
|
83
|
+
unreadable.push("the editor host cannot enumerate project files, so no scene source was read");
|
|
84
|
+
return { documents, unreadable, paths };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (const filePath of host.readDirectory(projectRoot, extensions)) {
|
|
88
|
+
const displayPath = displayPathOf(projectRoot, filePath);
|
|
89
|
+
if (isExcludedProjectPath(displayPath)) continue;
|
|
90
|
+
const text = host.readFile(filePath);
|
|
91
|
+
if (text === undefined) {
|
|
92
|
+
unreadable.push(`${displayPath}: could not be read`);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
documents.set(displayPath, text);
|
|
96
|
+
paths.push(filePath);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return { documents, unreadable, paths };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// The project's own files of the given kinds, as the `/`-prefixed
|
|
103
|
+
// project-relative paths a Defold resource property names. Nothing is read: a
|
|
104
|
+
// resource path is the whole suggestion, so the walk is the entire universe.
|
|
105
|
+
//
|
|
106
|
+
// The extension filter is re-applied rather than trusted. `readSceneDocuments`
|
|
107
|
+
// can take the host at its word because a wrong document merely contributes no
|
|
108
|
+
// ids, but here a wrong file *is* the suggestion — a host that ignored its
|
|
109
|
+
// `extensions` argument would turn a `.font` slot into a project-wide file dump.
|
|
110
|
+
export function listProjectResourcePaths(
|
|
111
|
+
host: SceneReadHost,
|
|
112
|
+
projectRoot: string,
|
|
113
|
+
extensions: readonly string[],
|
|
114
|
+
): Set<string> {
|
|
115
|
+
if (!host.readDirectory) return new Set();
|
|
116
|
+
|
|
117
|
+
const paths: string[] = [];
|
|
118
|
+
for (const filePath of host.readDirectory(projectRoot, extensions)) {
|
|
119
|
+
const displayPath = displayPathOf(projectRoot, filePath);
|
|
120
|
+
if (isExcludedProjectPath(displayPath)) continue;
|
|
121
|
+
if (!extensions.some((extension) => displayPath.endsWith(extension))) continue;
|
|
122
|
+
paths.push(`/${displayPath}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return new Set(paths.sort());
|
|
126
|
+
}
|