adnbn 0.7.1 → 0.8.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.
@@ -2,6 +2,7 @@ import path from "path";
2
2
  import fs from "fs";
3
3
  import { getAppPath, getAppSourcePath, getResolvePath, getSharedPath, getSourcePath } from "./../../../cli/resolvers/path.js";
4
4
  import AbstractFinder from "./AbstractFinder.js";
5
+ import { FileLayer, getWorkspaceFileLayers, setFilePrecedence } from "./utils/filePrecedence.js";
5
6
  class AbstractAssetFinder_default extends AbstractFinder {
6
7
  getDirectory() {
7
8
  return ".";
@@ -20,21 +21,31 @@ class AbstractAssetFinder_default extends AbstractFinder {
20
21
  return this.isValidName(name) && this.isValidExtension(ext);
21
22
  }
22
23
  async getFiles() {
23
- const files = /* @__PURE__ */ new Set();
24
- const parser = async (directory) => {
25
- if (files.size === 0 || this.canMerge()) {
26
- const localeFiles = await this.findFiles(getResolvePath(directory));
27
- for (const file of localeFiles) {
28
- files.add(file);
24
+ const files = /* @__PURE__ */ new Map();
25
+ const collect = async (directory, layer) => {
26
+ const assetFiles = await this.findFiles(getResolvePath(directory));
27
+ for (const file of assetFiles) {
28
+ const canonicalPath = fs.realpathSync.native(file.file);
29
+ if (!files.has(canonicalPath)) {
30
+ files.set(canonicalPath, setFilePrecedence(file, { layer }));
29
31
  }
30
32
  }
31
33
  };
32
34
  const dir = this.getDirectory();
33
- await parser(getAppSourcePath(this.config, dir));
34
- await parser(getAppPath(this.config, dir));
35
- await parser(getSharedPath(this.config, dir));
36
- await parser(getSourcePath(this.config, dir));
37
- return files;
35
+ const directories = {
36
+ [FileLayer.Source]: getSourcePath(this.config, dir),
37
+ [FileLayer.Shared]: getSharedPath(this.config, dir),
38
+ [FileLayer.App]: getAppPath(this.config, dir),
39
+ [FileLayer.AppSource]: getAppSourcePath(this.config, dir)
40
+ };
41
+ const merge = this.canMerge();
42
+ for (const layer of getWorkspaceFileLayers()) {
43
+ await collect(directories[layer], layer);
44
+ if (!merge && files.size > 0) {
45
+ break;
46
+ }
47
+ }
48
+ return new Set(files.values());
38
49
  }
39
50
  async findFiles(directory) {
40
51
  const files = /* @__PURE__ */ new Set();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/cli/entrypoint/finder/AbstractAssetFinder.ts"],"sourcesContent":["import path from \"path\";\nimport fs from \"fs\";\n\nimport {getAppPath, getAppSourcePath, getResolvePath, getSharedPath, getSourcePath} from \"@cli/resolvers/path\";\n\nimport AbstractFinder from \"./AbstractFinder\";\n\nimport {EntrypointFile} from \"@typing/entrypoint\";\n\nexport default abstract class extends AbstractFinder {\n public abstract getNames(): ReadonlySet<string>;\n\n public abstract isValidExtension(extension: string): boolean;\n\n public getDirectory(): string {\n return \".\";\n }\n\n public isValidName(name: string): boolean {\n return name.length > 0 && this.getNames().has(name);\n }\n\n public canMerge(): boolean {\n return false;\n }\n\n public isValidFilename(filename: string): boolean {\n let {name, ext} = path.parse(filename);\n\n if (ext.startsWith(\".\")) {\n ext = ext.slice(1);\n }\n\n return this.isValidName(name) && this.isValidExtension(ext);\n }\n\n protected async getFiles(): Promise<Set<EntrypointFile>> {\n const files = new Set<EntrypointFile>();\n\n const parser = async (directory: string): Promise<void> => {\n if (files.size === 0 || this.canMerge()) {\n const localeFiles = await this.findFiles(getResolvePath(directory));\n\n for (const file of localeFiles) {\n files.add(file);\n }\n }\n };\n\n const dir = this.getDirectory();\n\n await parser(getAppSourcePath(this.config, dir));\n await parser(getAppPath(this.config, dir));\n await parser(getSharedPath(this.config, dir));\n await parser(getSourcePath(this.config, dir));\n\n return files;\n }\n\n protected async findFiles(directory: string): Promise<Set<EntrypointFile>> {\n const files = new Set<EntrypointFile>();\n\n try {\n const entries = fs.readdirSync(directory);\n\n for (const entry of entries) {\n const fullPath = path.join(directory, entry);\n const stats = fs.statSync(fullPath);\n\n if (stats.isFile() && this.isValidFilename(fullPath)) {\n files.add(this.file(fullPath));\n }\n }\n } catch {}\n\n return files;\n }\n}\n"],"mappings":"AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AAEf,SAAQ,YAAY,kBAAkB,gBAAgB,eAAe,qBAAoB;AAEzF,OAAO,oBAAoB;AAI3B,MAAO,oCAA+B,eAAe;AAAA,EAK1C,eAAuB;AAC1B,WAAO;AAAA,EACX;AAAA,EAEO,YAAY,MAAuB;AACtC,WAAO,KAAK,SAAS,KAAK,KAAK,SAAS,EAAE,IAAI,IAAI;AAAA,EACtD;AAAA,EAEO,WAAoB;AACvB,WAAO;AAAA,EACX;AAAA,EAEO,gBAAgB,UAA2B;AAC9C,QAAI,EAAC,MAAM,IAAG,IAAI,KAAK,MAAM,QAAQ;AAErC,QAAI,IAAI,WAAW,GAAG,GAAG;AACrB,YAAM,IAAI,MAAM,CAAC;AAAA,IACrB;AAEA,WAAO,KAAK,YAAY,IAAI,KAAK,KAAK,iBAAiB,GAAG;AAAA,EAC9D;AAAA,EAEA,MAAgB,WAAyC;AACrD,UAAM,QAAQ,oBAAI,IAAoB;AAEtC,UAAM,SAAS,OAAO,cAAqC;AACvD,UAAI,MAAM,SAAS,KAAK,KAAK,SAAS,GAAG;AACrC,cAAM,cAAc,MAAM,KAAK,UAAU,eAAe,SAAS,CAAC;AAElE,mBAAW,QAAQ,aAAa;AAC5B,gBAAM,IAAI,IAAI;AAAA,QAClB;AAAA,MACJ;AAAA,IACJ;AAEA,UAAM,MAAM,KAAK,aAAa;AAE9B,UAAM,OAAO,iBAAiB,KAAK,QAAQ,GAAG,CAAC;AAC/C,UAAM,OAAO,WAAW,KAAK,QAAQ,GAAG,CAAC;AACzC,UAAM,OAAO,cAAc,KAAK,QAAQ,GAAG,CAAC;AAC5C,UAAM,OAAO,cAAc,KAAK,QAAQ,GAAG,CAAC;AAE5C,WAAO;AAAA,EACX;AAAA,EAEA,MAAgB,UAAU,WAAiD;AACvE,UAAM,QAAQ,oBAAI,IAAoB;AAEtC,QAAI;AACA,YAAM,UAAU,GAAG,YAAY,SAAS;AAExC,iBAAW,SAAS,SAAS;AACzB,cAAM,WAAW,KAAK,KAAK,WAAW,KAAK;AAC3C,cAAM,QAAQ,GAAG,SAAS,QAAQ;AAElC,YAAI,MAAM,OAAO,KAAK,KAAK,gBAAgB,QAAQ,GAAG;AAClD,gBAAM,IAAI,KAAK,KAAK,QAAQ,CAAC;AAAA,QACjC;AAAA,MACJ;AAAA,IACJ,QAAQ;AAAA,IAAC;AAET,WAAO;AAAA,EACX;AACJ;","names":[]}
1
+ {"version":3,"sources":["../../../../src/cli/entrypoint/finder/AbstractAssetFinder.ts"],"sourcesContent":["import path from \"path\";\nimport fs from \"fs\";\n\nimport {getAppPath, getAppSourcePath, getResolvePath, getSharedPath, getSourcePath} from \"@cli/resolvers/path\";\n\nimport AbstractFinder from \"./AbstractFinder\";\nimport {FileLayer, getWorkspaceFileLayers, setFilePrecedence, type WorkspaceFileLayer} from \"./utils/filePrecedence\";\n\nimport {EntrypointFile} from \"@typing/entrypoint\";\n\nexport default abstract class extends AbstractFinder {\n public abstract getNames(): ReadonlySet<string>;\n\n public abstract isValidExtension(extension: string): boolean;\n\n public getDirectory(): string {\n return \".\";\n }\n\n public isValidName(name: string): boolean {\n return name.length > 0 && this.getNames().has(name);\n }\n\n public canMerge(): boolean {\n return false;\n }\n\n public isValidFilename(filename: string): boolean {\n let {name, ext} = path.parse(filename);\n\n if (ext.startsWith(\".\")) {\n ext = ext.slice(1);\n }\n\n return this.isValidName(name) && this.isValidExtension(ext);\n }\n\n protected async getFiles(): Promise<Set<EntrypointFile>> {\n const files = new Map<string, EntrypointFile>();\n\n const collect = async (directory: string, layer: FileLayer): Promise<void> => {\n const assetFiles = await this.findFiles(getResolvePath(directory));\n\n for (const file of assetFiles) {\n const canonicalPath = fs.realpathSync.native(file.file);\n\n if (!files.has(canonicalPath)) {\n files.set(canonicalPath, setFilePrecedence(file, {layer}));\n }\n }\n };\n\n const dir = this.getDirectory();\n\n const directories: Record<WorkspaceFileLayer, string> = {\n [FileLayer.Source]: getSourcePath(this.config, dir),\n [FileLayer.Shared]: getSharedPath(this.config, dir),\n [FileLayer.App]: getAppPath(this.config, dir),\n [FileLayer.AppSource]: getAppSourcePath(this.config, dir),\n };\n\n const merge = this.canMerge();\n\n for (const layer of getWorkspaceFileLayers()) {\n await collect(directories[layer], layer);\n\n if (!merge && files.size > 0) {\n break;\n }\n }\n\n return new Set(files.values());\n }\n\n protected async findFiles(directory: string): Promise<Set<EntrypointFile>> {\n const files = new Set<EntrypointFile>();\n\n try {\n const entries = fs.readdirSync(directory);\n\n for (const entry of entries) {\n const fullPath = path.join(directory, entry);\n const stats = fs.statSync(fullPath);\n\n if (stats.isFile() && this.isValidFilename(fullPath)) {\n files.add(this.file(fullPath));\n }\n }\n } catch {}\n\n return files;\n }\n}\n"],"mappings":"AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AAEf,SAAQ,YAAY,kBAAkB,gBAAgB,eAAe,qBAAoB;AAEzF,OAAO,oBAAoB;AAC3B,SAAQ,WAAW,wBAAwB,yBAAiD;AAI5F,MAAO,oCAA+B,eAAe;AAAA,EAK1C,eAAuB;AAC1B,WAAO;AAAA,EACX;AAAA,EAEO,YAAY,MAAuB;AACtC,WAAO,KAAK,SAAS,KAAK,KAAK,SAAS,EAAE,IAAI,IAAI;AAAA,EACtD;AAAA,EAEO,WAAoB;AACvB,WAAO;AAAA,EACX;AAAA,EAEO,gBAAgB,UAA2B;AAC9C,QAAI,EAAC,MAAM,IAAG,IAAI,KAAK,MAAM,QAAQ;AAErC,QAAI,IAAI,WAAW,GAAG,GAAG;AACrB,YAAM,IAAI,MAAM,CAAC;AAAA,IACrB;AAEA,WAAO,KAAK,YAAY,IAAI,KAAK,KAAK,iBAAiB,GAAG;AAAA,EAC9D;AAAA,EAEA,MAAgB,WAAyC;AACrD,UAAM,QAAQ,oBAAI,IAA4B;AAE9C,UAAM,UAAU,OAAO,WAAmB,UAAoC;AAC1E,YAAM,aAAa,MAAM,KAAK,UAAU,eAAe,SAAS,CAAC;AAEjE,iBAAW,QAAQ,YAAY;AAC3B,cAAM,gBAAgB,GAAG,aAAa,OAAO,KAAK,IAAI;AAEtD,YAAI,CAAC,MAAM,IAAI,aAAa,GAAG;AAC3B,gBAAM,IAAI,eAAe,kBAAkB,MAAM,EAAC,MAAK,CAAC,CAAC;AAAA,QAC7D;AAAA,MACJ;AAAA,IACJ;AAEA,UAAM,MAAM,KAAK,aAAa;AAE9B,UAAM,cAAkD;AAAA,MACpD,CAAC,UAAU,MAAM,GAAG,cAAc,KAAK,QAAQ,GAAG;AAAA,MAClD,CAAC,UAAU,MAAM,GAAG,cAAc,KAAK,QAAQ,GAAG;AAAA,MAClD,CAAC,UAAU,GAAG,GAAG,WAAW,KAAK,QAAQ,GAAG;AAAA,MAC5C,CAAC,UAAU,SAAS,GAAG,iBAAiB,KAAK,QAAQ,GAAG;AAAA,IAC5D;AAEA,UAAM,QAAQ,KAAK,SAAS;AAE5B,eAAW,SAAS,uBAAuB,GAAG;AAC1C,YAAM,QAAQ,YAAY,KAAK,GAAG,KAAK;AAEvC,UAAI,CAAC,SAAS,MAAM,OAAO,GAAG;AAC1B;AAAA,MACJ;AAAA,IACJ;AAEA,WAAO,IAAI,IAAI,MAAM,OAAO,CAAC;AAAA,EACjC;AAAA,EAEA,MAAgB,UAAU,WAAiD;AACvE,UAAM,QAAQ,oBAAI,IAAoB;AAEtC,QAAI;AACA,YAAM,UAAU,GAAG,YAAY,SAAS;AAExC,iBAAW,SAAS,SAAS;AACzB,cAAM,WAAW,KAAK,KAAK,WAAW,KAAK;AAC3C,cAAM,QAAQ,GAAG,SAAS,QAAQ;AAElC,YAAI,MAAM,OAAO,KAAK,KAAK,gBAAgB,QAAQ,GAAG;AAClD,gBAAM,IAAI,KAAK,KAAK,QAAQ,CAAC;AAAA,QACjC;AAAA,MACJ;AAAA,IACJ,QAAQ;AAAA,IAAC;AAET,WAAO;AAAA,EACX;AACJ;","names":[]}
@@ -2,6 +2,7 @@ import fs from "fs";
2
2
  import path from "path";
3
3
  import pluralize from "pluralize";
4
4
  import AbstractOptionsFinder from "./AbstractOptionsFinder.js";
5
+ import { FileLayer, setFilePrecedence } from "./utils/filePrecedence.js";
5
6
  import { getAppSourcePath, getSharedPath } from "./../../../cli/resolvers/path.js";
6
7
  import { EntrypointFileExtensions } from "./../../../types/entrypoint.js";
7
8
  class AbstractEntrypointFinder_default extends AbstractOptionsFinder {
@@ -19,6 +20,9 @@ class AbstractEntrypointFinder_default extends AbstractOptionsFinder {
19
20
  const entrypoint = this.type();
20
21
  let files = /* @__PURE__ */ new Set();
21
22
  const appFiles = this.findFiles(getAppSourcePath(this.config));
23
+ for (const file of appFiles) {
24
+ setFilePrecedence(file, { layer: FileLayer.AppSource });
25
+ }
22
26
  if (appFiles.size > 0) {
23
27
  files = appFiles;
24
28
  if (this.config.debug) {
@@ -27,6 +31,9 @@ class AbstractEntrypointFinder_default extends AbstractOptionsFinder {
27
31
  }
28
32
  if (appFiles.size > 0 && this.canMerge() || appFiles.size === 0) {
29
33
  const sharedFiles = this.findFiles(getSharedPath(this.config));
34
+ for (const file of sharedFiles) {
35
+ setFilePrecedence(file, { layer: FileLayer.Shared });
36
+ }
30
37
  if (sharedFiles.size > 0) {
31
38
  files = /* @__PURE__ */ new Set([...files, ...sharedFiles]);
32
39
  if (this.config.debug) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/cli/entrypoint/finder/AbstractEntrypointFinder.ts"],"sourcesContent":["import fs, {Dirent} from \"fs\";\nimport path from \"path\";\nimport pluralize from \"pluralize\";\n\nimport AbstractOptionsFinder from \"./AbstractOptionsFinder\";\n\nimport {getAppSourcePath, getSharedPath} from \"@cli/resolvers/path\";\n\nimport {EntrypointFile, EntrypointFileExtensions, EntrypointOptions} from \"@typing/entrypoint\";\nimport {ReadonlyConfig} from \"@typing/config\";\n\nexport default abstract class<O extends EntrypointOptions> extends AbstractOptionsFinder<O> {\n protected fileExtensionsPattern: string;\n\n protected possibleIndexFiles: Set<string>;\n\n protected constructor(config: ReadonlyConfig) {\n super(config);\n\n this.fileExtensionsPattern = [...EntrypointFileExtensions].map(ext => ext.replace(\".\", \"\\\\.\")).join(\"|\");\n\n this.possibleIndexFiles = new Set([...EntrypointFileExtensions].map(ext => `index.${ext}`));\n }\n\n public canMerge(): boolean {\n return false;\n }\n\n protected async getFiles(): Promise<Set<EntrypointFile>> {\n const entrypoint = this.type();\n\n let files = new Set<EntrypointFile>();\n\n const appFiles = this.findFiles(getAppSourcePath(this.config));\n\n if (appFiles.size > 0) {\n files = appFiles;\n\n if (this.config.debug) {\n console.info(`App \"${entrypoint}\" files added:`, appFiles);\n }\n }\n\n if ((appFiles.size > 0 && this.canMerge()) || appFiles.size === 0) {\n const sharedFiles = this.findFiles(getSharedPath(this.config));\n\n if (sharedFiles.size > 0) {\n files = new Set([...files, ...sharedFiles]);\n\n if (this.config.debug) {\n console.info(`Shared \"${entrypoint}\" files added:`, sharedFiles);\n }\n }\n }\n\n return files;\n }\n\n protected findFiles(directory: string): Set<EntrypointFile> {\n const entrypoint = this.type();\n const entrypointPluralize = pluralize(entrypoint);\n\n const rootFiles: EntrypointFile[] = [];\n const groupedFiles: EntrypointFile[] = [];\n\n const collect = (dir: string, files: EntrypointFile[], collectGrouped: boolean): void => {\n let entries: Dirent[];\n\n try {\n entries = fs.readdirSync(dir, {withFileTypes: true});\n } catch (e) {\n if (this.config.debug) {\n console.log(`Failed to read ${entrypoint} entrypoint directory: \"${dir}\". Skipping.`);\n }\n\n return;\n }\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n\n if (entry.isDirectory()) {\n if (this.isValidDirname(entry.name)) {\n for (const indexFile of this.possibleIndexFiles) {\n const indexPath = path.join(fullPath, indexFile);\n\n try {\n const stat = fs.statSync(indexPath);\n\n if (stat.isFile()) {\n files.push(this.file(indexPath));\n }\n } catch (e) {\n if (this.config.debug) {\n console.log(\n `Error reading ${entrypoint} entrypoint index file: \"${indexPath}\". Skipping.`\n );\n }\n }\n }\n } else if (collectGrouped && entry.name === entrypointPluralize) {\n collect(fullPath, groupedFiles, false);\n }\n } else if (entry.isFile() && this.isValidFilename(entry.name)) {\n files.push(this.file(fullPath));\n }\n }\n };\n\n collect(directory, rootFiles, true);\n\n if (groupedFiles.length > 0) {\n return new Set(groupedFiles);\n }\n\n if (rootFiles.length > 0) {\n return new Set(rootFiles);\n }\n\n try {\n directory = path.join(directory, entrypoint);\n\n const stat = fs.statSync(directory);\n\n if (stat.isDirectory()) {\n collect(directory, rootFiles, false);\n }\n } catch (e) {\n if (this.config.debug) {\n console.log(\"Error reading entrypoint directory:\", directory);\n }\n }\n\n return new Set(rootFiles);\n }\n\n protected isValidFilename(filename: string): boolean {\n const name = this.type().replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\n const pattern = new RegExp(`^(?:.*\\\\.)?${name}\\\\.(${this.fileExtensionsPattern})$`);\n\n return pattern.test(filename);\n }\n\n protected isValidDirname(dirname: string): boolean {\n const entrypoint = this.type();\n\n return dirname === entrypoint || dirname.endsWith(`.${entrypoint}`);\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/cli/entrypoint/finder/AbstractEntrypointFinder.ts"],"sourcesContent":["import fs, {Dirent} from \"fs\";\nimport path from \"path\";\nimport pluralize from \"pluralize\";\n\nimport AbstractOptionsFinder from \"./AbstractOptionsFinder\";\nimport {FileLayer, setFilePrecedence} from \"./utils/filePrecedence\";\n\nimport {getAppSourcePath, getSharedPath} from \"@cli/resolvers/path\";\n\nimport {EntrypointFile, EntrypointFileExtensions, EntrypointOptions} from \"@typing/entrypoint\";\nimport {ReadonlyConfig} from \"@typing/config\";\n\nexport default abstract class<O extends EntrypointOptions> extends AbstractOptionsFinder<O> {\n protected fileExtensionsPattern: string;\n\n protected possibleIndexFiles: Set<string>;\n\n protected constructor(config: ReadonlyConfig) {\n super(config);\n\n this.fileExtensionsPattern = [...EntrypointFileExtensions].map(ext => ext.replace(\".\", \"\\\\.\")).join(\"|\");\n\n this.possibleIndexFiles = new Set([...EntrypointFileExtensions].map(ext => `index.${ext}`));\n }\n\n public canMerge(): boolean {\n return false;\n }\n\n protected async getFiles(): Promise<Set<EntrypointFile>> {\n const entrypoint = this.type();\n\n let files = new Set<EntrypointFile>();\n\n const appFiles = this.findFiles(getAppSourcePath(this.config));\n\n for (const file of appFiles) {\n setFilePrecedence(file, {layer: FileLayer.AppSource});\n }\n\n if (appFiles.size > 0) {\n files = appFiles;\n\n if (this.config.debug) {\n console.info(`App \"${entrypoint}\" files added:`, appFiles);\n }\n }\n\n if ((appFiles.size > 0 && this.canMerge()) || appFiles.size === 0) {\n const sharedFiles = this.findFiles(getSharedPath(this.config));\n\n for (const file of sharedFiles) {\n setFilePrecedence(file, {layer: FileLayer.Shared});\n }\n\n if (sharedFiles.size > 0) {\n files = new Set([...files, ...sharedFiles]);\n\n if (this.config.debug) {\n console.info(`Shared \"${entrypoint}\" files added:`, sharedFiles);\n }\n }\n }\n\n return files;\n }\n\n protected findFiles(directory: string): Set<EntrypointFile> {\n const entrypoint = this.type();\n const entrypointPluralize = pluralize(entrypoint);\n\n const rootFiles: EntrypointFile[] = [];\n const groupedFiles: EntrypointFile[] = [];\n\n const collect = (dir: string, files: EntrypointFile[], collectGrouped: boolean): void => {\n let entries: Dirent[];\n\n try {\n entries = fs.readdirSync(dir, {withFileTypes: true});\n } catch (e) {\n if (this.config.debug) {\n console.log(`Failed to read ${entrypoint} entrypoint directory: \"${dir}\". Skipping.`);\n }\n\n return;\n }\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n\n if (entry.isDirectory()) {\n if (this.isValidDirname(entry.name)) {\n for (const indexFile of this.possibleIndexFiles) {\n const indexPath = path.join(fullPath, indexFile);\n\n try {\n const stat = fs.statSync(indexPath);\n\n if (stat.isFile()) {\n files.push(this.file(indexPath));\n }\n } catch (e) {\n if (this.config.debug) {\n console.log(\n `Error reading ${entrypoint} entrypoint index file: \"${indexPath}\". Skipping.`\n );\n }\n }\n }\n } else if (collectGrouped && entry.name === entrypointPluralize) {\n collect(fullPath, groupedFiles, false);\n }\n } else if (entry.isFile() && this.isValidFilename(entry.name)) {\n files.push(this.file(fullPath));\n }\n }\n };\n\n collect(directory, rootFiles, true);\n\n if (groupedFiles.length > 0) {\n return new Set(groupedFiles);\n }\n\n if (rootFiles.length > 0) {\n return new Set(rootFiles);\n }\n\n try {\n directory = path.join(directory, entrypoint);\n\n const stat = fs.statSync(directory);\n\n if (stat.isDirectory()) {\n collect(directory, rootFiles, false);\n }\n } catch (e) {\n if (this.config.debug) {\n console.log(\"Error reading entrypoint directory:\", directory);\n }\n }\n\n return new Set(rootFiles);\n }\n\n protected isValidFilename(filename: string): boolean {\n const name = this.type().replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\n const pattern = new RegExp(`^(?:.*\\\\.)?${name}\\\\.(${this.fileExtensionsPattern})$`);\n\n return pattern.test(filename);\n }\n\n protected isValidDirname(dirname: string): boolean {\n const entrypoint = this.type();\n\n return dirname === entrypoint || dirname.endsWith(`.${entrypoint}`);\n }\n}\n"],"mappings":"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","names":[]}
@@ -4,7 +4,6 @@ export default abstract class implements EntrypointFinder {
4
4
  protected readonly config: ReadonlyConfig;
5
5
  protected _files?: Set<EntrypointFile>;
6
6
  private readonly require;
7
- protected readonly priorityDirectories: string[];
8
7
  protected abstract getFiles(): Promise<Set<EntrypointFile>>;
9
8
  protected constructor(config: ReadonlyConfig);
10
9
  clear(): this;
@@ -15,6 +14,5 @@ export default abstract class implements EntrypointFinder {
15
14
  protected file(filename: string): EntrypointFile;
16
15
  protected resolve(name: string, filename: string): EntrypointFile;
17
16
  protected resolveSafely(name: string, filename: string): EntrypointFile | undefined;
18
- protected priority(file: EntrypointFile): number;
19
17
  protected sortKey(file: EntrypointFile): string;
20
18
  }
@@ -1,34 +1,19 @@
1
- import _ from "lodash";
2
1
  import path from "path";
3
2
  import fs from "fs";
4
3
  import { createRequire } from "module";
5
4
  import { fileURLToPath } from "url";
5
+ import { compareFilePrecedence } from "./utils/filePrecedence.js";
6
6
  import { toPosixPath } from "./../../../cli/utils/path.js";
7
7
  import { isFile } from "./../../../cli/utils/fs.js";
8
- import {
9
- getAppPath,
10
- getAppSourcePath,
11
- getResolvePath,
12
- getSharedPath,
13
- getSourcePath,
14
- resolveRootPath
15
- } from "./../../../cli/resolvers/path.js";
8
+ import { getResolvePath, getSourcePath, resolveRootPath } from "./../../../cli/resolvers/path.js";
16
9
  import { resolveAssetsPath, resolveEntrypointPath } from "./../../../cli/entrypoint/utils/index.js";
17
10
  class AbstractFinder_default {
18
11
  constructor(config) {
19
12
  this.config = config;
20
- this.priorityDirectories = [
21
- "node_modules",
22
- getSourcePath(config),
23
- getSharedPath(config),
24
- getAppPath(config),
25
- getAppSourcePath(config)
26
- ];
27
13
  }
28
14
  config;
29
15
  _files;
30
16
  require = createRequire(import.meta.url);
31
- priorityDirectories;
32
17
  clear() {
33
18
  this._files = void 0;
34
19
  return this;
@@ -38,10 +23,9 @@ class AbstractFinder_default {
38
23
  return this._files;
39
24
  }
40
25
  const files = Array.from(await this.getFiles()).sort((a, b) => {
41
- const priorityA = this.priority(a);
42
- const priorityB = this.priority(b);
43
- if (priorityA !== priorityB) {
44
- return priorityA - priorityB;
26
+ const precedence = compareFilePrecedence(a, b);
27
+ if (precedence !== 0) {
28
+ return precedence;
45
29
  }
46
30
  return this.sortKey(a).localeCompare(this.sortKey(b));
47
31
  });
@@ -100,10 +84,6 @@ class AbstractFinder_default {
100
84
  return void 0;
101
85
  }
102
86
  }
103
- priority(file) {
104
- const priority = _.findIndex(this.priorityDirectories, (dir) => file.file.includes(dir));
105
- return priority >= 0 ? priority : this.priorityDirectories.length;
106
- }
107
87
  sortKey(file) {
108
88
  return toPosixPath(file.import || file.file);
109
89
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/cli/entrypoint/finder/AbstractFinder.ts"],"sourcesContent":["import _ from \"lodash\";\nimport path from \"path\";\nimport fs from \"fs\";\nimport {createRequire} from \"module\";\nimport {fileURLToPath} from \"url\";\n\nimport {toPosixPath} from \"@cli/utils/path\";\nimport {isFile} from \"@cli/utils/fs\";\nimport {\n getAppPath,\n getAppSourcePath,\n getResolvePath,\n getSharedPath,\n getSourcePath,\n resolveRootPath,\n} from \"@cli/resolvers/path\";\nimport {resolveAssetsPath, resolveEntrypointPath} from \"@cli/entrypoint/utils\";\n\nimport {ReadonlyConfig} from \"@typing/config\";\nimport {EntrypointFile, EntrypointFinder} from \"@typing/entrypoint\";\n\nexport default abstract class implements EntrypointFinder {\n protected _files?: Set<EntrypointFile>;\n\n private readonly require = createRequire(import.meta.url);\n\n protected readonly priorityDirectories: string[];\n\n protected abstract getFiles(): Promise<Set<EntrypointFile>>;\n\n protected constructor(protected readonly config: ReadonlyConfig) {\n this.priorityDirectories = [\n \"node_modules\",\n getSourcePath(config),\n getSharedPath(config),\n getAppPath(config),\n getAppSourcePath(config),\n ];\n }\n\n public clear(): this {\n this._files = undefined;\n\n return this;\n }\n\n public async files(): Promise<Set<EntrypointFile>> {\n if (this._files) {\n return this._files;\n }\n\n const files = Array.from(await this.getFiles()).sort((a, b) => {\n const priorityA = this.priority(a);\n const priorityB = this.priority(b);\n\n if (priorityA !== priorityB) {\n return priorityA - priorityB;\n }\n\n return this.sortKey(a).localeCompare(this.sortKey(b));\n });\n\n return (this._files = new Set(files));\n }\n\n public async empty(): Promise<boolean> {\n return (await this.files()).size === 0;\n }\n\n public async exists(): Promise<boolean> {\n return !(await this.empty());\n }\n\n public holds(file: EntrypointFile): boolean {\n return this._files?.has(file) || false;\n }\n\n protected file(filename: string): EntrypointFile {\n return {file: filename, import: toPosixPath(filename)};\n }\n\n protected resolve(name: string, filename: string): EntrypointFile {\n let base: string = name.startsWith(\"file://\") ? fileURLToPath(name) : name;\n\n if (path.isAbsolute(base)) {\n if (isFile(base)) {\n base = path.dirname(base);\n }\n\n const absBase = path.join(base, filename);\n\n let resolved: string | undefined = isFile(absBase)\n ? absBase\n : (resolveEntrypointPath(absBase) ?? resolveAssetsPath(absBase));\n\n if (!resolved) {\n throw new Error(`Cannot resolve entrypoint file \"${filename}\" from \"${name}\"`);\n }\n\n resolved = fs.realpathSync.native(resolved);\n\n const srcDir = getResolvePath(getSourcePath(this.config));\n\n return {\n file: resolved,\n /**\n * Global alias \"@/\":\n * baked into the framework by the TypeScript plugin\n * (src/cli/plugins/typescript) and points to the sources directory (srcDir).\n * Here we build a relative path from srcDir so that the import looks like \"@/relative/path\".\n * @type {string}\n */\n import: \"@/\" + toPosixPath(path.relative(srcDir, resolved)),\n };\n }\n\n const spec: string = path.posix.join(name, filename);\n\n const file = this.require.resolve(spec, {paths: [resolveRootPath(this.config)]});\n\n return {\n file,\n import: spec,\n external: name,\n };\n }\n\n protected resolveSafely(name: string, filename: string): EntrypointFile | undefined {\n try {\n return this.resolve(name, filename);\n } catch {\n return undefined;\n }\n }\n\n protected priority(file: EntrypointFile): number {\n const priority = _.findIndex(this.priorityDirectories, dir => file.file.includes(dir));\n\n return priority >= 0 ? priority : this.priorityDirectories.length;\n }\n\n protected sortKey(file: EntrypointFile): string {\n return toPosixPath(file.import || file.file);\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/cli/entrypoint/finder/AbstractFinder.ts"],"sourcesContent":["import path from \"path\";\nimport fs from \"fs\";\nimport {createRequire} from \"module\";\nimport {fileURLToPath} from \"url\";\n\nimport {compareFilePrecedence} from \"./utils/filePrecedence\";\n\nimport {toPosixPath} from \"@cli/utils/path\";\nimport {isFile} from \"@cli/utils/fs\";\nimport {getResolvePath, getSourcePath, resolveRootPath} from \"@cli/resolvers/path\";\nimport {resolveAssetsPath, resolveEntrypointPath} from \"@cli/entrypoint/utils\";\n\nimport {ReadonlyConfig} from \"@typing/config\";\nimport {EntrypointFile, EntrypointFinder} from \"@typing/entrypoint\";\n\nexport default abstract class implements EntrypointFinder {\n protected _files?: Set<EntrypointFile>;\n\n private readonly require = createRequire(import.meta.url);\n\n protected abstract getFiles(): Promise<Set<EntrypointFile>>;\n\n protected constructor(protected readonly config: ReadonlyConfig) {}\n\n public clear(): this {\n this._files = undefined;\n\n return this;\n }\n\n public async files(): Promise<Set<EntrypointFile>> {\n if (this._files) {\n return this._files;\n }\n\n const files = Array.from(await this.getFiles()).sort((a, b) => {\n const precedence = compareFilePrecedence(a, b);\n\n if (precedence !== 0) {\n return precedence;\n }\n\n return this.sortKey(a).localeCompare(this.sortKey(b));\n });\n\n return (this._files = new Set(files));\n }\n\n public async empty(): Promise<boolean> {\n return (await this.files()).size === 0;\n }\n\n public async exists(): Promise<boolean> {\n return !(await this.empty());\n }\n\n public holds(file: EntrypointFile): boolean {\n return this._files?.has(file) || false;\n }\n\n protected file(filename: string): EntrypointFile {\n return {file: filename, import: toPosixPath(filename)};\n }\n\n protected resolve(name: string, filename: string): EntrypointFile {\n let base: string = name.startsWith(\"file://\") ? fileURLToPath(name) : name;\n\n if (path.isAbsolute(base)) {\n if (isFile(base)) {\n base = path.dirname(base);\n }\n\n const absBase = path.join(base, filename);\n\n let resolved: string | undefined = isFile(absBase)\n ? absBase\n : (resolveEntrypointPath(absBase) ?? resolveAssetsPath(absBase));\n\n if (!resolved) {\n throw new Error(`Cannot resolve entrypoint file \"${filename}\" from \"${name}\"`);\n }\n\n resolved = fs.realpathSync.native(resolved);\n\n const srcDir = getResolvePath(getSourcePath(this.config));\n\n return {\n file: resolved,\n /**\n * Global alias \"@/\":\n * baked into the framework by the TypeScript plugin\n * (src/cli/plugins/typescript) and points to the sources directory (srcDir).\n * Here we build a relative path from srcDir so that the import looks like \"@/relative/path\".\n * @type {string}\n */\n import: \"@/\" + toPosixPath(path.relative(srcDir, resolved)),\n };\n }\n\n const spec: string = path.posix.join(name, filename);\n\n const file = this.require.resolve(spec, {paths: [resolveRootPath(this.config)]});\n\n return {\n file,\n import: spec,\n external: name,\n };\n }\n\n protected resolveSafely(name: string, filename: string): EntrypointFile | undefined {\n try {\n return this.resolve(name, filename);\n } catch {\n return undefined;\n }\n }\n\n protected sortKey(file: EntrypointFile): string {\n return toPosixPath(file.import || file.file);\n }\n}\n"],"mappings":"AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,SAAQ,qBAAoB;AAC5B,SAAQ,qBAAoB;AAE5B,SAAQ,6BAA4B;AAEpC,SAAQ,mBAAkB;AAC1B,SAAQ,cAAa;AACrB,SAAQ,gBAAgB,eAAe,uBAAsB;AAC7D,SAAQ,mBAAmB,6BAA4B;AAKvD,MAAO,uBAAmD;AAAA,EAO5C,YAA+B,QAAwB;AAAxB;AAAA,EAAyB;AAAA,EAAzB;AAAA,EAN/B;AAAA,EAEO,UAAU,cAAc,YAAY,GAAG;AAAA,EAMjD,QAAc;AACjB,SAAK,SAAS;AAEd,WAAO;AAAA,EACX;AAAA,EAEA,MAAa,QAAsC;AAC/C,QAAI,KAAK,QAAQ;AACb,aAAO,KAAK;AAAA,IAChB;AAEA,UAAM,QAAQ,MAAM,KAAK,MAAM,KAAK,SAAS,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM;AAC3D,YAAM,aAAa,sBAAsB,GAAG,CAAC;AAE7C,UAAI,eAAe,GAAG;AAClB,eAAO;AAAA,MACX;AAEA,aAAO,KAAK,QAAQ,CAAC,EAAE,cAAc,KAAK,QAAQ,CAAC,CAAC;AAAA,IACxD,CAAC;AAED,WAAQ,KAAK,SAAS,IAAI,IAAI,KAAK;AAAA,EACvC;AAAA,EAEA,MAAa,QAA0B;AACnC,YAAQ,MAAM,KAAK,MAAM,GAAG,SAAS;AAAA,EACzC;AAAA,EAEA,MAAa,SAA2B;AACpC,WAAO,CAAE,MAAM,KAAK,MAAM;AAAA,EAC9B;AAAA,EAEO,MAAM,MAA+B;AAxDhD;AAyDQ,aAAO,UAAK,WAAL,mBAAa,IAAI,UAAS;AAAA,EACrC;AAAA,EAEU,KAAK,UAAkC;AAC7C,WAAO,EAAC,MAAM,UAAU,QAAQ,YAAY,QAAQ,EAAC;AAAA,EACzD;AAAA,EAEU,QAAQ,MAAc,UAAkC;AAC9D,QAAI,OAAe,KAAK,WAAW,SAAS,IAAI,cAAc,IAAI,IAAI;AAEtE,QAAI,KAAK,WAAW,IAAI,GAAG;AACvB,UAAI,OAAO,IAAI,GAAG;AACd,eAAO,KAAK,QAAQ,IAAI;AAAA,MAC5B;AAEA,YAAM,UAAU,KAAK,KAAK,MAAM,QAAQ;AAExC,UAAI,WAA+B,OAAO,OAAO,IAC3C,UACC,sBAAsB,OAAO,KAAK,kBAAkB,OAAO;AAElE,UAAI,CAAC,UAAU;AACX,cAAM,IAAI,MAAM,mCAAmC,QAAQ,WAAW,IAAI,GAAG;AAAA,MACjF;AAEA,iBAAW,GAAG,aAAa,OAAO,QAAQ;AAE1C,YAAM,SAAS,eAAe,cAAc,KAAK,MAAM,CAAC;AAExD,aAAO;AAAA,QACH,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAQN,QAAQ,OAAO,YAAY,KAAK,SAAS,QAAQ,QAAQ,CAAC;AAAA,MAC9D;AAAA,IACJ;AAEA,UAAM,OAAe,KAAK,MAAM,KAAK,MAAM,QAAQ;AAEnD,UAAM,OAAO,KAAK,QAAQ,QAAQ,MAAM,EAAC,OAAO,CAAC,gBAAgB,KAAK,MAAM,CAAC,EAAC,CAAC;AAE/E,WAAO;AAAA,MACH;AAAA,MACA,QAAQ;AAAA,MACR,UAAU;AAAA,IACd;AAAA,EACJ;AAAA,EAEU,cAAc,MAAc,UAA8C;AAChF,QAAI;AACA,aAAO,KAAK,QAAQ,MAAM,QAAQ;AAAA,IACtC,QAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AAAA,EAEU,QAAQ,MAA8B;AAC5C,WAAO,YAAY,KAAK,UAAU,KAAK,IAAI;AAAA,EAC/C;AACJ;","names":[]}
@@ -23,16 +23,15 @@ class AbstractViewFinder_default extends AbstractPluginFinder {
23
23
  }
24
24
  async getViews() {
25
25
  const views = /* @__PURE__ */ new Map();
26
- for (const [file, options] of await this.plugin().options()) {
26
+ const entries = [...await this.plugin().options()];
27
+ const candidates = this.allowMultiple() ? entries : entries.slice(-1);
28
+ for (const [file, options] of candidates) {
27
29
  views.set(this.createViewName(file, options), {
28
30
  alias: this.createViewAlias(file, options),
29
31
  filename: this.createViewFilename(file, options),
30
32
  file,
31
33
  options
32
34
  });
33
- if (!this.allowMultiple()) {
34
- break;
35
- }
36
35
  }
37
36
  return views;
38
37
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/cli/entrypoint/finder/AbstractViewFinder.ts"],"sourcesContent":["import path from \"path\";\n\nimport {InlineNameGenerator, NameGenerator} from \"../name\";\n\nimport AbstractPluginFinder from \"./AbstractPluginFinder\";\n\nimport {ReadonlyConfig} from \"@typing/config\";\nimport {ViewEntrypointOptions} from \"@typing/view\";\nimport {EntrypointFile, EntrypointNameGenerator} from \"@typing/entrypoint\";\n\nexport interface ViewItem<O extends ViewEntrypointOptions> {\n filename: string;\n alias: string;\n file: EntrypointFile;\n options: O;\n}\n\nexport type ViewItems<O extends ViewEntrypointOptions> = Map<string, ViewItem<O>>;\n\nexport type ViewAliasToFilename = Map<string, string>;\n\nexport type ViewFileToFilename = Map<EntrypointFile, string>;\n\nexport default abstract class<O extends ViewEntrypointOptions> extends AbstractPluginFinder<O> {\n protected _views?: ViewItems<O>;\n\n protected readonly names: EntrypointNameGenerator;\n protected readonly aliases: EntrypointNameGenerator;\n protected readonly filenames: EntrypointNameGenerator;\n\n protected constructor(config: ReadonlyConfig) {\n super(config);\n\n this.names = this.createNameGenerator();\n this.aliases = this.createAliasGenerator();\n this.filenames = this.createFilenameGenerator();\n }\n\n protected createNameGenerator(): EntrypointNameGenerator {\n return new NameGenerator(this.type());\n }\n\n protected createAliasGenerator(): EntrypointNameGenerator {\n return new InlineNameGenerator(this.type());\n }\n\n protected createFilenameGenerator(): EntrypointNameGenerator {\n return new NameGenerator(this.type());\n }\n\n protected async getViews(): Promise<ViewItems<O>> {\n const views: ViewItems<O> = new Map();\n\n for (const [file, options] of await this.plugin().options()) {\n views.set(this.createViewName(file, options), {\n alias: this.createViewAlias(file, options),\n filename: this.createViewFilename(file, options),\n file,\n options,\n });\n\n if (!this.allowMultiple()) {\n break;\n }\n }\n\n return views;\n }\n\n protected createViewFilename(file: EntrypointFile, options: O): string {\n const {as} = options;\n\n const filename = as ? this.filenames.name(as) : this.filenames.file(file);\n\n return path.posix.join(this.config.htmlDir, filename + \".html\");\n }\n\n protected createViewAlias(file: EntrypointFile, options: O): string {\n const {as} = options;\n\n let alias = as ? this.aliases.name(as) : this.aliases.file(file);\n\n if (file.external) {\n alias = file.import;\n }\n\n return alias;\n }\n\n protected createViewName(file: EntrypointFile, options: O): string {\n const {as} = options;\n\n return as ? this.names.name(as) : this.names.file(file);\n }\n\n public async views(): Promise<ViewItems<O>> {\n return (this._views ??= await this.getViews());\n }\n\n public allowMultiple(): boolean {\n return true;\n }\n\n public async getAliasToFilename(): Promise<ViewAliasToFilename> {\n return Array.from(await this.views()).reduce((aliases, [_, item]) => {\n return {\n ...aliases,\n [item.alias]: item.filename,\n };\n }, new Map() as ViewAliasToFilename);\n }\n\n public async getAlias(): Promise<Set<string>> {\n const aliases = new Set<string>();\n\n const views = await this.views();\n\n for (const item of views.values()) {\n aliases.add(item.alias);\n }\n\n return aliases;\n }\n\n public async getFilenames(): Promise<ViewFileToFilename> {\n const views = await this.views();\n\n const filenames: ViewFileToFilename = new Map();\n\n for (const {file, filename} of views.values()) {\n filenames.set(file, filename);\n }\n\n return filenames;\n }\n\n public clear(): this {\n this._views = undefined;\n\n this.names.reset();\n this.aliases.reset();\n this.filenames.reset();\n\n return super.clear();\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/cli/entrypoint/finder/AbstractViewFinder.ts"],"sourcesContent":["import path from \"path\";\n\nimport {InlineNameGenerator, NameGenerator} from \"../name\";\n\nimport AbstractPluginFinder from \"./AbstractPluginFinder\";\n\nimport {ReadonlyConfig} from \"@typing/config\";\nimport {ViewEntrypointOptions} from \"@typing/view\";\nimport {EntrypointFile, EntrypointNameGenerator} from \"@typing/entrypoint\";\n\nexport interface ViewItem<O extends ViewEntrypointOptions> {\n filename: string;\n alias: string;\n file: EntrypointFile;\n options: O;\n}\n\nexport type ViewItems<O extends ViewEntrypointOptions> = Map<string, ViewItem<O>>;\n\nexport type ViewAliasToFilename = Map<string, string>;\n\nexport type ViewFileToFilename = Map<EntrypointFile, string>;\n\nexport default abstract class<O extends ViewEntrypointOptions> extends AbstractPluginFinder<O> {\n protected _views?: ViewItems<O>;\n\n protected readonly names: EntrypointNameGenerator;\n protected readonly aliases: EntrypointNameGenerator;\n protected readonly filenames: EntrypointNameGenerator;\n\n protected constructor(config: ReadonlyConfig) {\n super(config);\n\n this.names = this.createNameGenerator();\n this.aliases = this.createAliasGenerator();\n this.filenames = this.createFilenameGenerator();\n }\n\n protected createNameGenerator(): EntrypointNameGenerator {\n return new NameGenerator(this.type());\n }\n\n protected createAliasGenerator(): EntrypointNameGenerator {\n return new InlineNameGenerator(this.type());\n }\n\n protected createFilenameGenerator(): EntrypointNameGenerator {\n return new NameGenerator(this.type());\n }\n\n protected async getViews(): Promise<ViewItems<O>> {\n const views: ViewItems<O> = new Map();\n const entries = [...(await this.plugin().options())];\n const candidates = this.allowMultiple() ? entries : entries.slice(-1);\n\n for (const [file, options] of candidates) {\n views.set(this.createViewName(file, options), {\n alias: this.createViewAlias(file, options),\n filename: this.createViewFilename(file, options),\n file,\n options,\n });\n }\n\n return views;\n }\n\n protected createViewFilename(file: EntrypointFile, options: O): string {\n const {as} = options;\n\n const filename = as ? this.filenames.name(as) : this.filenames.file(file);\n\n return path.posix.join(this.config.htmlDir, filename + \".html\");\n }\n\n protected createViewAlias(file: EntrypointFile, options: O): string {\n const {as} = options;\n\n let alias = as ? this.aliases.name(as) : this.aliases.file(file);\n\n if (file.external) {\n alias = file.import;\n }\n\n return alias;\n }\n\n protected createViewName(file: EntrypointFile, options: O): string {\n const {as} = options;\n\n return as ? this.names.name(as) : this.names.file(file);\n }\n\n public async views(): Promise<ViewItems<O>> {\n return (this._views ??= await this.getViews());\n }\n\n public allowMultiple(): boolean {\n return true;\n }\n\n public async getAliasToFilename(): Promise<ViewAliasToFilename> {\n return Array.from(await this.views()).reduce((aliases, [_, item]) => {\n return {\n ...aliases,\n [item.alias]: item.filename,\n };\n }, new Map() as ViewAliasToFilename);\n }\n\n public async getAlias(): Promise<Set<string>> {\n const aliases = new Set<string>();\n\n const views = await this.views();\n\n for (const item of views.values()) {\n aliases.add(item.alias);\n }\n\n return aliases;\n }\n\n public async getFilenames(): Promise<ViewFileToFilename> {\n const views = await this.views();\n\n const filenames: ViewFileToFilename = new Map();\n\n for (const {file, filename} of views.values()) {\n filenames.set(file, filename);\n }\n\n return filenames;\n }\n\n public clear(): this {\n this._views = undefined;\n\n this.names.reset();\n this.aliases.reset();\n this.filenames.reset();\n\n return super.clear();\n }\n}\n"],"mappings":"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","names":[]}
@@ -3,6 +3,7 @@ import path from "path";
3
3
  import pluralize from "pluralize";
4
4
  import { processPluginHandler } from "./../../../cli/resolvers/plugin.js";
5
5
  import AbstractFinder from "./AbstractFinder.js";
6
+ import { FileLayer, FileSpecificity, setFilePrecedence, setFileSpecificity } from "./utils/filePrecedence.js";
6
7
  class AssetPluginFinder_default extends AbstractFinder {
7
8
  constructor(config, key, finder) {
8
9
  super(config);
@@ -18,7 +19,8 @@ class AssetPluginFinder_default extends AbstractFinder {
18
19
  })
19
20
  );
20
21
  const files = /* @__PURE__ */ new Set();
21
- for await (let { name, result } of pluginResult) {
22
+ for (let pluginIndex = 0; pluginIndex < pluginResult.length; pluginIndex++) {
23
+ let { name, result } = pluginResult[pluginIndex];
22
24
  if (_.isBoolean(result)) {
23
25
  result = pluralize(this.key);
24
26
  }
@@ -26,6 +28,7 @@ class AssetPluginFinder_default extends AbstractFinder {
26
28
  result = await this.findFiles(name, result);
27
29
  }
28
30
  if (_.isArray(result) || _.isSet(result)) {
31
+ let sequence = 0;
29
32
  for (const item of result) {
30
33
  if (_.isEmpty(item)) {
31
34
  continue;
@@ -45,7 +48,17 @@ class AssetPluginFinder_default extends AbstractFinder {
45
48
  }
46
49
  const { name: filename } = path.parse(file.file);
47
50
  if (filename.endsWith(`.${this.config.browser}`) || !filename.includes(".")) {
51
+ setFilePrecedence(file, {
52
+ layer: FileLayer.Plugin,
53
+ order: pluginIndex,
54
+ sequence
55
+ });
56
+ setFileSpecificity(
57
+ file,
58
+ filename.endsWith(`.${this.config.browser}`) ? FileSpecificity.Browser : FileSpecificity.Generic
59
+ );
48
60
  files.add(file);
61
+ sequence++;
49
62
  }
50
63
  }
51
64
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/cli/entrypoint/finder/AssetPluginFinder.ts"],"sourcesContent":["import _ from \"lodash\";\nimport path from \"path\";\nimport pluralize from \"pluralize\";\n\nimport {processPluginHandler} from \"@cli/resolvers/plugin\";\n\nimport AbstractFinder from \"./AbstractFinder\";\nimport AbstractAssetFinder from \"./AbstractAssetFinder\";\n\nimport {EntrypointFile} from \"@typing/entrypoint\";\nimport {ReadonlyConfig} from \"@typing/config\";\nimport {PluginAssetKeys} from \"@typing/plugin\";\n\nexport default class extends AbstractFinder {\n constructor(\n config: ReadonlyConfig,\n protected readonly key: PluginAssetKeys,\n protected readonly finder: AbstractAssetFinder\n ) {\n super(config);\n }\n\n protected async getFiles(): Promise<Set<EntrypointFile>> {\n const pluginResult = await Array.fromAsync(\n processPluginHandler(this.config.plugins, this.key, {\n config: this.config,\n })\n );\n\n const files = new Set<EntrypointFile>();\n\n for await (let {name, result} of pluginResult) {\n if (_.isBoolean(result)) {\n result = pluralize(this.key);\n }\n\n if (_.isString(result)) {\n result = await this.findFiles(name, result);\n }\n\n if (_.isArray(result) || _.isSet(result)) {\n for (const item of result) {\n if (_.isEmpty(item)) {\n continue;\n }\n\n let file: EntrypointFile;\n\n if (_.isString(item) && this.finder.isValidFilename(item)) {\n file = this.file(item);\n } else if (_.isPlainObject(item)) {\n const {file: filename} = item as EntrypointFile;\n\n if (filename && this.finder.isValidFilename(filename)) {\n file = item as EntrypointFile;\n } else {\n continue;\n }\n } else {\n continue;\n }\n\n const {name: filename} = path.parse(file.file);\n\n if (filename.endsWith(`.${this.config.browser}`) || !filename.includes(\".\")) {\n files.add(file);\n }\n }\n }\n }\n\n return files;\n }\n\n protected async findFiles(name: string, directory: string): Promise<Set<EntrypointFile>> {\n const files = new Set<EntrypointFile>();\n const resolve = (file: string) => this.resolveSafely(name, path.posix.join(directory, file));\n\n for (const name of this.finder.getNames()) {\n for (const file of [resolve(name), resolve(`${name}.${this.config.browser}`)]) {\n if (file) {\n files.add(file);\n }\n }\n }\n\n return files;\n }\n}\n"],"mappings":"AAAA,OAAO,OAAO;AACd,OAAO,UAAU;AACjB,OAAO,eAAe;AAEtB,SAAQ,4BAA2B;AAEnC,OAAO,oBAAoB;AAO3B,MAAO,kCAAsB,eAAe;AAAA,EACxC,YACI,QACmB,KACA,QACrB;AACE,UAAM,MAAM;AAHO;AACA;AAAA,EAGvB;AAAA,EAJuB;AAAA,EACA;AAAA,EAKvB,MAAgB,WAAyC;AACrD,UAAM,eAAe,MAAM,MAAM;AAAA,MAC7B,qBAAqB,KAAK,OAAO,SAAS,KAAK,KAAK;AAAA,QAChD,QAAQ,KAAK;AAAA,MACjB,CAAC;AAAA,IACL;AAEA,UAAM,QAAQ,oBAAI,IAAoB;AAEtC,mBAAe,EAAC,MAAM,OAAM,KAAK,cAAc;AAC3C,UAAI,EAAE,UAAU,MAAM,GAAG;AACrB,iBAAS,UAAU,KAAK,GAAG;AAAA,MAC/B;AAEA,UAAI,EAAE,SAAS,MAAM,GAAG;AACpB,iBAAS,MAAM,KAAK,UAAU,MAAM,MAAM;AAAA,MAC9C;AAEA,UAAI,EAAE,QAAQ,MAAM,KAAK,EAAE,MAAM,MAAM,GAAG;AACtC,mBAAW,QAAQ,QAAQ;AACvB,cAAI,EAAE,QAAQ,IAAI,GAAG;AACjB;AAAA,UACJ;AAEA,cAAI;AAEJ,cAAI,EAAE,SAAS,IAAI,KAAK,KAAK,OAAO,gBAAgB,IAAI,GAAG;AACvD,mBAAO,KAAK,KAAK,IAAI;AAAA,UACzB,WAAW,EAAE,cAAc,IAAI,GAAG;AAC9B,kBAAM,EAAC,MAAMA,UAAQ,IAAI;AAEzB,gBAAIA,aAAY,KAAK,OAAO,gBAAgBA,SAAQ,GAAG;AACnD,qBAAO;AAAA,YACX,OAAO;AACH;AAAA,YACJ;AAAA,UACJ,OAAO;AACH;AAAA,UACJ;AAEA,gBAAM,EAAC,MAAM,SAAQ,IAAI,KAAK,MAAM,KAAK,IAAI;AAE7C,cAAI,SAAS,SAAS,IAAI,KAAK,OAAO,OAAO,EAAE,KAAK,CAAC,SAAS,SAAS,GAAG,GAAG;AACzE,kBAAM,IAAI,IAAI;AAAA,UAClB;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAEA,WAAO;AAAA,EACX;AAAA,EAEA,MAAgB,UAAU,MAAc,WAAiD;AACrF,UAAM,QAAQ,oBAAI,IAAoB;AACtC,UAAM,UAAU,CAAC,SAAiB,KAAK,cAAc,MAAM,KAAK,MAAM,KAAK,WAAW,IAAI,CAAC;AAE3F,eAAWC,SAAQ,KAAK,OAAO,SAAS,GAAG;AACvC,iBAAW,QAAQ,CAAC,QAAQA,KAAI,GAAG,QAAQ,GAAGA,KAAI,IAAI,KAAK,OAAO,OAAO,EAAE,CAAC,GAAG;AAC3E,YAAI,MAAM;AACN,gBAAM,IAAI,IAAI;AAAA,QAClB;AAAA,MACJ;AAAA,IACJ;AAEA,WAAO;AAAA,EACX;AACJ;","names":["filename","name"]}
1
+ {"version":3,"sources":["../../../../src/cli/entrypoint/finder/AssetPluginFinder.ts"],"sourcesContent":["import _ from \"lodash\";\nimport path from \"path\";\nimport pluralize from \"pluralize\";\n\nimport {processPluginHandler} from \"@cli/resolvers/plugin\";\n\nimport AbstractFinder from \"./AbstractFinder\";\nimport AbstractAssetFinder from \"./AbstractAssetFinder\";\nimport {FileLayer, FileSpecificity, setFilePrecedence, setFileSpecificity} from \"./utils/filePrecedence\";\n\nimport {EntrypointFile} from \"@typing/entrypoint\";\nimport {ReadonlyConfig} from \"@typing/config\";\nimport {PluginAssetKeys} from \"@typing/plugin\";\n\nexport default class extends AbstractFinder {\n constructor(\n config: ReadonlyConfig,\n protected readonly key: PluginAssetKeys,\n protected readonly finder: AbstractAssetFinder\n ) {\n super(config);\n }\n\n protected async getFiles(): Promise<Set<EntrypointFile>> {\n const pluginResult = await Array.fromAsync(\n processPluginHandler(this.config.plugins, this.key, {\n config: this.config,\n })\n );\n\n // Plugin files preserve registration identity, order and sequence.\n // Canonical-path deduplication is intentionally limited to overlapping\n // workspace paths in AbstractAssetFinder.\n const files = new Set<EntrypointFile>();\n\n for (let pluginIndex = 0; pluginIndex < pluginResult.length; pluginIndex++) {\n let {name, result} = pluginResult[pluginIndex];\n\n if (_.isBoolean(result)) {\n result = pluralize(this.key);\n }\n\n if (_.isString(result)) {\n result = await this.findFiles(name, result);\n }\n\n if (_.isArray(result) || _.isSet(result)) {\n let sequence = 0;\n\n for (const item of result) {\n if (_.isEmpty(item)) {\n continue;\n }\n\n let file: EntrypointFile;\n\n if (_.isString(item) && this.finder.isValidFilename(item)) {\n file = this.file(item);\n } else if (_.isPlainObject(item)) {\n const {file: filename} = item as EntrypointFile;\n\n if (filename && this.finder.isValidFilename(filename)) {\n file = item as EntrypointFile;\n } else {\n continue;\n }\n } else {\n continue;\n }\n\n const {name: filename} = path.parse(file.file);\n\n if (filename.endsWith(`.${this.config.browser}`) || !filename.includes(\".\")) {\n setFilePrecedence(file, {\n layer: FileLayer.Plugin,\n order: pluginIndex,\n sequence,\n });\n setFileSpecificity(\n file,\n filename.endsWith(`.${this.config.browser}`)\n ? FileSpecificity.Browser\n : FileSpecificity.Generic\n );\n files.add(file);\n sequence++;\n }\n }\n }\n }\n\n return files;\n }\n\n protected async findFiles(name: string, directory: string): Promise<Set<EntrypointFile>> {\n const files = new Set<EntrypointFile>();\n const resolve = (file: string) => this.resolveSafely(name, path.posix.join(directory, file));\n\n for (const name of this.finder.getNames()) {\n for (const file of [resolve(name), resolve(`${name}.${this.config.browser}`)]) {\n if (file) {\n files.add(file);\n }\n }\n }\n\n return files;\n }\n}\n"],"mappings":"AAAA,OAAO,OAAO;AACd,OAAO,UAAU;AACjB,OAAO,eAAe;AAEtB,SAAQ,4BAA2B;AAEnC,OAAO,oBAAoB;AAE3B,SAAQ,WAAW,iBAAiB,mBAAmB,0BAAyB;AAMhF,MAAO,kCAAsB,eAAe;AAAA,EACxC,YACI,QACmB,KACA,QACrB;AACE,UAAM,MAAM;AAHO;AACA;AAAA,EAGvB;AAAA,EAJuB;AAAA,EACA;AAAA,EAKvB,MAAgB,WAAyC;AACrD,UAAM,eAAe,MAAM,MAAM;AAAA,MAC7B,qBAAqB,KAAK,OAAO,SAAS,KAAK,KAAK;AAAA,QAChD,QAAQ,KAAK;AAAA,MACjB,CAAC;AAAA,IACL;AAKA,UAAM,QAAQ,oBAAI,IAAoB;AAEtC,aAAS,cAAc,GAAG,cAAc,aAAa,QAAQ,eAAe;AACxE,UAAI,EAAC,MAAM,OAAM,IAAI,aAAa,WAAW;AAE7C,UAAI,EAAE,UAAU,MAAM,GAAG;AACrB,iBAAS,UAAU,KAAK,GAAG;AAAA,MAC/B;AAEA,UAAI,EAAE,SAAS,MAAM,GAAG;AACpB,iBAAS,MAAM,KAAK,UAAU,MAAM,MAAM;AAAA,MAC9C;AAEA,UAAI,EAAE,QAAQ,MAAM,KAAK,EAAE,MAAM,MAAM,GAAG;AACtC,YAAI,WAAW;AAEf,mBAAW,QAAQ,QAAQ;AACvB,cAAI,EAAE,QAAQ,IAAI,GAAG;AACjB;AAAA,UACJ;AAEA,cAAI;AAEJ,cAAI,EAAE,SAAS,IAAI,KAAK,KAAK,OAAO,gBAAgB,IAAI,GAAG;AACvD,mBAAO,KAAK,KAAK,IAAI;AAAA,UACzB,WAAW,EAAE,cAAc,IAAI,GAAG;AAC9B,kBAAM,EAAC,MAAMA,UAAQ,IAAI;AAEzB,gBAAIA,aAAY,KAAK,OAAO,gBAAgBA,SAAQ,GAAG;AACnD,qBAAO;AAAA,YACX,OAAO;AACH;AAAA,YACJ;AAAA,UACJ,OAAO;AACH;AAAA,UACJ;AAEA,gBAAM,EAAC,MAAM,SAAQ,IAAI,KAAK,MAAM,KAAK,IAAI;AAE7C,cAAI,SAAS,SAAS,IAAI,KAAK,OAAO,OAAO,EAAE,KAAK,CAAC,SAAS,SAAS,GAAG,GAAG;AACzE,8BAAkB,MAAM;AAAA,cACpB,OAAO,UAAU;AAAA,cACjB,OAAO;AAAA,cACP;AAAA,YACJ,CAAC;AACD;AAAA,cACI;AAAA,cACA,SAAS,SAAS,IAAI,KAAK,OAAO,OAAO,EAAE,IACrC,gBAAgB,UAChB,gBAAgB;AAAA,YAC1B;AACA,kBAAM,IAAI,IAAI;AACd;AAAA,UACJ;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAEA,WAAO;AAAA,EACX;AAAA,EAEA,MAAgB,UAAU,MAAc,WAAiD;AACrF,UAAM,QAAQ,oBAAI,IAAoB;AACtC,UAAM,UAAU,CAAC,SAAiB,KAAK,cAAc,MAAM,KAAK,MAAM,KAAK,WAAW,IAAI,CAAC;AAE3F,eAAWC,SAAQ,KAAK,OAAO,SAAS,GAAG;AACvC,iBAAW,QAAQ,CAAC,QAAQA,KAAI,GAAG,QAAQ,GAAGA,KAAI,IAAI,KAAK,OAAO,OAAO,EAAE,CAAC,GAAG;AAC3E,YAAI,MAAM;AACN,gBAAM,IAAI,IAAI;AAAA,QAClB;AAAA,MACJ;AAAA,IACJ;AAEA,WAAO;AAAA,EACX;AACJ;","names":["filename","name"]}
@@ -2,6 +2,7 @@ import AbstractAssetFinder from "./AbstractAssetFinder.js";
2
2
  import AssetPluginFinder from "./AssetPluginFinder.js";
3
3
  import { LocaleStructureValidator } from "../../builders/locale/index.js";
4
4
  import { ReadonlyConfig } from "../../../types/config.js";
5
+ import { EntrypointFile } from "../../../types/entrypoint.js";
5
6
  import { Language, LocaleBuilders, LocaleKeys, LocaleStructure } from "../../../types/locale.js";
6
7
  export type { LocaleBuilders } from "../../../types/locale.js";
7
8
  export default class extends AbstractAssetFinder {
@@ -18,6 +19,7 @@ export default class extends AbstractAssetFinder {
18
19
  protected getPlugin(): AssetPluginFinder;
19
20
  plugin(): AssetPluginFinder;
20
21
  protected getBuilders(): Promise<LocaleBuilders>;
22
+ protected assertUniqueFiles(lang: Language, files: EntrypointFile[]): void;
21
23
  protected getValidator(): LocaleStructureValidator;
22
24
  protected getLanguageFromFilename(filename: string): Language;
23
25
  builders(): Promise<LocaleBuilders>;
@@ -4,12 +4,24 @@ import fs from "fs";
4
4
  import yaml from "js-yaml";
5
5
  import AbstractAssetFinder from "./AbstractAssetFinder.js";
6
6
  import AssetPluginFinder from "./AssetPluginFinder.js";
7
+ import { FileLayer, FileSpecificity, getFilePrecedence } from "./utils/filePrecedence.js";
7
8
  import localeFactory, { LocaleStructureValidator } from "./../../../cli/builders/locale/index.js";
8
9
  import { isFileExtension } from "./../../../cli/utils/path.js";
9
10
  import {
10
11
  LanguageCodes,
11
12
  LocaleFileExtensions
12
13
  } from "./../../../types/locale.js";
14
+ const layerNames = {
15
+ [FileLayer.Plugin]: "plugin",
16
+ [FileLayer.Source]: "source",
17
+ [FileLayer.Shared]: "shared",
18
+ [FileLayer.App]: "app",
19
+ [FileLayer.AppSource]: "app source"
20
+ };
21
+ const specificityNames = {
22
+ [FileSpecificity.Generic]: "generic",
23
+ [FileSpecificity.Browser]: "browser-specific"
24
+ };
13
25
  class LocaleFinder_default extends AbstractAssetFinder {
14
26
  _plugin;
15
27
  _builders;
@@ -44,6 +56,7 @@ class LocaleFinder_default extends AbstractAssetFinder {
44
56
  }
45
57
  async getBuilders() {
46
58
  return _.chain(Array.from(await this.plugin().files())).groupBy((file) => this.getLanguageFromFilename(file.file)).reduce((map, files, lang) => {
59
+ this.assertUniqueFiles(lang, files);
47
60
  const locale = localeFactory(lang, this.config);
48
61
  for (const { file } of files) {
49
62
  const content = fs.readFileSync(file, "utf8");
@@ -57,6 +70,20 @@ class LocaleFinder_default extends AbstractAssetFinder {
57
70
  return map;
58
71
  }, /* @__PURE__ */ new Map()).value();
59
72
  }
73
+ assertUniqueFiles(lang, files) {
74
+ const sources = /* @__PURE__ */ new Map();
75
+ for (const file of files) {
76
+ const { layer, order, specificity } = getFilePrecedence(file);
77
+ const source = [layer, order, specificity].join(":");
78
+ const duplicate = sources.get(source);
79
+ if (duplicate) {
80
+ throw new Error(
81
+ `Locale "${lang}" has multiple ${specificityNames[specificity]} files in the ${layerNames[layer]} layer: "${duplicate.file}" and "${file.file}"`
82
+ );
83
+ }
84
+ sources.set(source, file);
85
+ }
86
+ }
60
87
  getValidator() {
61
88
  return this._validator ?? (this._validator = new LocaleStructureValidator(this.config.lang));
62
89
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/cli/entrypoint/finder/LocaleFinder.ts"],"sourcesContent":["import _ from \"lodash\";\nimport path from \"path\";\nimport fs from \"fs\";\nimport yaml from \"js-yaml\";\n\nimport AbstractAssetFinder from \"./AbstractAssetFinder\";\nimport AssetPluginFinder from \"./AssetPluginFinder\";\n\nimport localeFactory, {LocaleStructureValidator} from \"@cli/builders/locale\";\nimport {isFileExtension} from \"@cli/utils/path\";\n\nimport {ReadonlyConfig} from \"@typing/config\";\nimport {\n Language,\n LanguageCodes,\n LocaleBuilders,\n LocaleData,\n LocaleFileExtensions,\n LocaleKeys,\n LocaleStructure,\n} from \"@typing/locale\";\n\nexport type {LocaleBuilders} from \"@typing/locale\";\n\nexport default class extends AbstractAssetFinder {\n protected _plugin?: AssetPluginFinder;\n protected _builders?: LocaleBuilders;\n protected _validator?: LocaleStructureValidator;\n protected _validated = false;\n\n public constructor(config: ReadonlyConfig) {\n super(config);\n }\n\n public isValidName(name: string): boolean {\n if (name.includes(\".\")) {\n name = name.split(\".\").slice(0, -1).join(\".\");\n }\n\n return super.isValidName(name);\n }\n\n public isValidExtension(extension: string): boolean {\n return LocaleFileExtensions.has(extension);\n }\n\n public getDirectory(): string {\n return this.config.localeDir || \"locales\";\n }\n\n public getNames(): ReadonlySet<string> {\n return LanguageCodes;\n }\n\n public canMerge(): boolean {\n return this.config.mergeLocales;\n }\n\n protected getPlugin(): AssetPluginFinder {\n return new AssetPluginFinder(this.config, \"locale\", this);\n }\n\n public plugin(): AssetPluginFinder {\n return (this._plugin ??= this.getPlugin());\n }\n\n protected async getBuilders(): Promise<LocaleBuilders> {\n return _.chain(Array.from(await this.plugin().files()))\n .groupBy(file => this.getLanguageFromFilename(file.file))\n .reduce((map, files, lang) => {\n const locale = localeFactory(lang as Language, this.config);\n\n for (const {file} of files) {\n const content = fs.readFileSync(file, \"utf8\");\n\n if (isFileExtension(file, [\"yaml\", \"yml\"])) {\n locale.merge(yaml.load(content) as LocaleData);\n } else if (isFileExtension(file, \"json\")) {\n locale.merge(JSON.parse(content));\n }\n }\n\n map.set(locale.lang(), locale);\n\n return map;\n }, new Map() as LocaleBuilders)\n .value();\n }\n\n protected getValidator(): LocaleStructureValidator {\n return (this._validator ??= new LocaleStructureValidator(this.config.lang));\n }\n\n protected getLanguageFromFilename(filename: string): Language {\n let {name} = path.parse(filename);\n\n if (name.includes(\".\")) {\n name = name.split(\".\").slice(0, -1).join(\".\");\n }\n\n if (LanguageCodes.has(name)) {\n return name as Language;\n }\n\n throw new Error(`Invalid locale filename: ${filename}`);\n }\n\n public async builders(): Promise<LocaleBuilders> {\n if (this._builders) {\n return this._builders;\n }\n\n return (this._builders = await this.getBuilders());\n }\n\n public async validate(): Promise<this> {\n if (!this._validated) {\n this.getValidator().validate(await this.builders());\n this._validated = true;\n }\n\n return this;\n }\n\n public async keys(): Promise<LocaleKeys> {\n const builders = await this.builders();\n const builder = this.getValidator().getDefaultBuilder(builders);\n\n return builder?.keys() ?? new Set();\n }\n\n public async structure(): Promise<LocaleStructure> {\n const builders = await this.builders();\n const builder = this.getValidator().getDefaultBuilder(builders);\n\n return builder?.structure() ?? {};\n }\n\n public async languages(): Promise<Set<Language>> {\n const builders = await this.builders();\n\n return new Set(builders.keys());\n }\n\n async empty(): Promise<boolean> {\n return this.plugin().empty();\n }\n\n public clear(): this {\n this.plugin().clear();\n\n this._builders = undefined;\n this._validator = undefined;\n this._validated = false;\n\n return super.clear();\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/cli/entrypoint/finder/LocaleFinder.ts"],"sourcesContent":["import _ from \"lodash\";\nimport path from \"path\";\nimport fs from \"fs\";\nimport yaml from \"js-yaml\";\n\nimport AbstractAssetFinder from \"./AbstractAssetFinder\";\nimport AssetPluginFinder from \"./AssetPluginFinder\";\nimport {FileLayer, FileSpecificity, getFilePrecedence} from \"./utils/filePrecedence\";\n\nimport localeFactory, {LocaleStructureValidator} from \"@cli/builders/locale\";\nimport {isFileExtension} from \"@cli/utils/path\";\n\nimport {ReadonlyConfig} from \"@typing/config\";\nimport {EntrypointFile} from \"@typing/entrypoint\";\nimport {\n Language,\n LanguageCodes,\n LocaleBuilders,\n LocaleData,\n LocaleFileExtensions,\n LocaleKeys,\n LocaleStructure,\n} from \"@typing/locale\";\n\nexport type {LocaleBuilders} from \"@typing/locale\";\n\nconst layerNames: Record<FileLayer, string> = {\n [FileLayer.Plugin]: \"plugin\",\n [FileLayer.Source]: \"source\",\n [FileLayer.Shared]: \"shared\",\n [FileLayer.App]: \"app\",\n [FileLayer.AppSource]: \"app source\",\n};\n\nconst specificityNames: Record<FileSpecificity, string> = {\n [FileSpecificity.Generic]: \"generic\",\n [FileSpecificity.Browser]: \"browser-specific\",\n};\n\nexport default class extends AbstractAssetFinder {\n protected _plugin?: AssetPluginFinder;\n protected _builders?: LocaleBuilders;\n protected _validator?: LocaleStructureValidator;\n protected _validated = false;\n\n public constructor(config: ReadonlyConfig) {\n super(config);\n }\n\n public isValidName(name: string): boolean {\n if (name.includes(\".\")) {\n name = name.split(\".\").slice(0, -1).join(\".\");\n }\n\n return super.isValidName(name);\n }\n\n public isValidExtension(extension: string): boolean {\n return LocaleFileExtensions.has(extension);\n }\n\n public getDirectory(): string {\n return this.config.localeDir || \"locales\";\n }\n\n public getNames(): ReadonlySet<string> {\n return LanguageCodes;\n }\n\n public canMerge(): boolean {\n return this.config.mergeLocales;\n }\n\n protected getPlugin(): AssetPluginFinder {\n return new AssetPluginFinder(this.config, \"locale\", this);\n }\n\n public plugin(): AssetPluginFinder {\n return (this._plugin ??= this.getPlugin());\n }\n\n protected async getBuilders(): Promise<LocaleBuilders> {\n return _.chain(Array.from(await this.plugin().files()))\n .groupBy(file => this.getLanguageFromFilename(file.file))\n .reduce((map, files, lang) => {\n this.assertUniqueFiles(lang as Language, files);\n\n const locale = localeFactory(lang as Language, this.config);\n\n for (const {file} of files) {\n const content = fs.readFileSync(file, \"utf8\");\n\n if (isFileExtension(file, [\"yaml\", \"yml\"])) {\n locale.merge(yaml.load(content) as LocaleData);\n } else if (isFileExtension(file, \"json\")) {\n locale.merge(JSON.parse(content));\n }\n }\n\n map.set(locale.lang(), locale);\n\n return map;\n }, new Map() as LocaleBuilders)\n .value();\n }\n\n protected assertUniqueFiles(lang: Language, files: EntrypointFile[]): void {\n const sources = new Map<string, EntrypointFile>();\n\n for (const file of files) {\n const {layer, order, specificity} = getFilePrecedence(file);\n const source = [layer, order, specificity].join(\":\");\n const duplicate = sources.get(source);\n\n if (duplicate) {\n throw new Error(\n `Locale \"${lang}\" has multiple ${specificityNames[specificity]} files in the ${layerNames[layer]} layer: \"${duplicate.file}\" and \"${file.file}\"`\n );\n }\n\n sources.set(source, file);\n }\n }\n\n protected getValidator(): LocaleStructureValidator {\n return (this._validator ??= new LocaleStructureValidator(this.config.lang));\n }\n\n protected getLanguageFromFilename(filename: string): Language {\n let {name} = path.parse(filename);\n\n if (name.includes(\".\")) {\n name = name.split(\".\").slice(0, -1).join(\".\");\n }\n\n if (LanguageCodes.has(name)) {\n return name as Language;\n }\n\n throw new Error(`Invalid locale filename: ${filename}`);\n }\n\n public async builders(): Promise<LocaleBuilders> {\n if (this._builders) {\n return this._builders;\n }\n\n return (this._builders = await this.getBuilders());\n }\n\n public async validate(): Promise<this> {\n if (!this._validated) {\n this.getValidator().validate(await this.builders());\n this._validated = true;\n }\n\n return this;\n }\n\n public async keys(): Promise<LocaleKeys> {\n const builders = await this.builders();\n const builder = this.getValidator().getDefaultBuilder(builders);\n\n return builder?.keys() ?? new Set();\n }\n\n public async structure(): Promise<LocaleStructure> {\n const builders = await this.builders();\n const builder = this.getValidator().getDefaultBuilder(builders);\n\n return builder?.structure() ?? {};\n }\n\n public async languages(): Promise<Set<Language>> {\n const builders = await this.builders();\n\n return new Set(builders.keys());\n }\n\n async empty(): Promise<boolean> {\n return this.plugin().empty();\n }\n\n public clear(): this {\n this.plugin().clear();\n\n this._builders = undefined;\n this._validator = undefined;\n this._validated = false;\n\n return super.clear();\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,6 @@
1
1
  import _ from "lodash";
2
2
  import AbstractOptionsFinder from "./AbstractOptionsFinder.js";
3
+ import { FileLayer, setFilePrecedence } from "./utils/filePrecedence.js";
3
4
  import { processPluginHandler } from "./../../../cli/resolvers/plugin.js";
4
5
  class PluginFinder_default extends AbstractOptionsFinder {
5
6
  constructor(config, key, finder) {
@@ -22,7 +23,8 @@ class PluginFinder_default extends AbstractOptionsFinder {
22
23
  })
23
24
  );
24
25
  const files = /* @__PURE__ */ new Set();
25
- for (const { name, result } of pluginResult) {
26
+ for (let pluginIndex = 0; pluginIndex < pluginResult.length; pluginIndex++) {
27
+ const { name, result } = pluginResult[pluginIndex];
26
28
  let endpoints = [];
27
29
  if (_.isBoolean(result)) {
28
30
  endpoints = [this.key];
@@ -33,8 +35,15 @@ class PluginFinder_default extends AbstractOptionsFinder {
33
35
  } else if (_.isSet(result)) {
34
36
  endpoints = Array.from(result);
35
37
  }
36
- for (const endpoint of endpoints) {
37
- files.add(_.isString(endpoint) ? this.resolve(name, endpoint) : endpoint);
38
+ for (let sequence = 0; sequence < endpoints.length; sequence++) {
39
+ const endpoint = endpoints[sequence];
40
+ const file = _.isString(endpoint) ? this.resolve(name, endpoint) : endpoint;
41
+ setFilePrecedence(file, {
42
+ layer: FileLayer.Plugin,
43
+ order: pluginIndex,
44
+ sequence
45
+ });
46
+ files.add(file);
38
47
  }
39
48
  }
40
49
  return files;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/cli/entrypoint/finder/PluginFinder.ts"],"sourcesContent":["import _ from \"lodash\";\n\nimport AbstractOptionsFinder from \"./AbstractOptionsFinder\";\n\nimport {processPluginHandler} from \"@cli/resolvers/plugin\";\n\nimport {ReadonlyConfig} from \"@typing/config\";\nimport {PluginHandlerKeys} from \"@typing/plugin\";\nimport {EntrypointFile, EntrypointOptions, EntrypointParser, EntrypointType} from \"@typing/entrypoint\";\n\nexport default class<O extends EntrypointOptions> extends AbstractOptionsFinder<O> {\n constructor(\n config: ReadonlyConfig,\n protected readonly key: PluginHandlerKeys,\n protected readonly finder: AbstractOptionsFinder<O>\n ) {\n super(config);\n }\n\n public type(): EntrypointType {\n return this.finder.type();\n }\n\n protected getParser(): EntrypointParser<O> {\n return this.finder.parser();\n }\n\n protected async getFiles(): Promise<Set<EntrypointFile>> {\n const pluginResult = await Array.fromAsync(\n processPluginHandler(this.config.plugins, this.key, {\n config: this.config,\n })\n );\n\n const files = new Set<EntrypointFile>();\n\n for (const {name, result} of pluginResult) {\n let endpoints: Array<string | EntrypointFile> = [];\n\n if (_.isBoolean(result)) {\n endpoints = [this.key];\n } else if (_.isString(result) || _.isPlainObject(result)) {\n endpoints = [result as string | EntrypointFile];\n } else if (_.isArray(result)) {\n endpoints = result;\n } else if (_.isSet(result)) {\n endpoints = Array.from(result as Set<EntrypointFile>);\n }\n\n for (const endpoint of endpoints) {\n files.add(_.isString(endpoint) ? this.resolve(name, endpoint) : endpoint);\n }\n }\n\n return files;\n }\n}\n"],"mappings":"AAAA,OAAO,OAAO;AAEd,OAAO,2BAA2B;AAElC,SAAQ,4BAA2B;AAMnC,MAAO,6BAAmD,sBAAyB;AAAA,EAC/E,YACI,QACmB,KACA,QACrB;AACE,UAAM,MAAM;AAHO;AACA;AAAA,EAGvB;AAAA,EAJuB;AAAA,EACA;AAAA,EAKhB,OAAuB;AAC1B,WAAO,KAAK,OAAO,KAAK;AAAA,EAC5B;AAAA,EAEU,YAAiC;AACvC,WAAO,KAAK,OAAO,OAAO;AAAA,EAC9B;AAAA,EAEA,MAAgB,WAAyC;AACrD,UAAM,eAAe,MAAM,MAAM;AAAA,MAC7B,qBAAqB,KAAK,OAAO,SAAS,KAAK,KAAK;AAAA,QAChD,QAAQ,KAAK;AAAA,MACjB,CAAC;AAAA,IACL;AAEA,UAAM,QAAQ,oBAAI,IAAoB;AAEtC,eAAW,EAAC,MAAM,OAAM,KAAK,cAAc;AACvC,UAAI,YAA4C,CAAC;AAEjD,UAAI,EAAE,UAAU,MAAM,GAAG;AACrB,oBAAY,CAAC,KAAK,GAAG;AAAA,MACzB,WAAW,EAAE,SAAS,MAAM,KAAK,EAAE,cAAc,MAAM,GAAG;AACtD,oBAAY,CAAC,MAAiC;AAAA,MAClD,WAAW,EAAE,QAAQ,MAAM,GAAG;AAC1B,oBAAY;AAAA,MAChB,WAAW,EAAE,MAAM,MAAM,GAAG;AACxB,oBAAY,MAAM,KAAK,MAA6B;AAAA,MACxD;AAEA,iBAAW,YAAY,WAAW;AAC9B,cAAM,IAAI,EAAE,SAAS,QAAQ,IAAI,KAAK,QAAQ,MAAM,QAAQ,IAAI,QAAQ;AAAA,MAC5E;AAAA,IACJ;AAEA,WAAO;AAAA,EACX;AACJ;","names":[]}
1
+ {"version":3,"sources":["../../../../src/cli/entrypoint/finder/PluginFinder.ts"],"sourcesContent":["import _ from \"lodash\";\n\nimport AbstractOptionsFinder from \"./AbstractOptionsFinder\";\nimport {FileLayer, setFilePrecedence} from \"./utils/filePrecedence\";\n\nimport {processPluginHandler} from \"@cli/resolvers/plugin\";\n\nimport {ReadonlyConfig} from \"@typing/config\";\nimport {PluginHandlerKeys} from \"@typing/plugin\";\nimport {EntrypointFile, EntrypointOptions, EntrypointParser, EntrypointType} from \"@typing/entrypoint\";\n\nexport default class<O extends EntrypointOptions> extends AbstractOptionsFinder<O> {\n constructor(\n config: ReadonlyConfig,\n protected readonly key: PluginHandlerKeys,\n protected readonly finder: AbstractOptionsFinder<O>\n ) {\n super(config);\n }\n\n public type(): EntrypointType {\n return this.finder.type();\n }\n\n protected getParser(): EntrypointParser<O> {\n return this.finder.parser();\n }\n\n protected async getFiles(): Promise<Set<EntrypointFile>> {\n const pluginResult = await Array.fromAsync(\n processPluginHandler(this.config.plugins, this.key, {\n config: this.config,\n })\n );\n\n const files = new Set<EntrypointFile>();\n\n for (let pluginIndex = 0; pluginIndex < pluginResult.length; pluginIndex++) {\n const {name, result} = pluginResult[pluginIndex];\n let endpoints: Array<string | EntrypointFile> = [];\n\n if (_.isBoolean(result)) {\n endpoints = [this.key];\n } else if (_.isString(result) || _.isPlainObject(result)) {\n endpoints = [result as string | EntrypointFile];\n } else if (_.isArray(result)) {\n endpoints = result;\n } else if (_.isSet(result)) {\n endpoints = Array.from(result as Set<EntrypointFile>);\n }\n\n for (let sequence = 0; sequence < endpoints.length; sequence++) {\n const endpoint = endpoints[sequence];\n const file = _.isString(endpoint) ? this.resolve(name, endpoint) : endpoint;\n\n setFilePrecedence(file, {\n layer: FileLayer.Plugin,\n order: pluginIndex,\n sequence,\n });\n files.add(file);\n }\n }\n\n return files;\n }\n}\n"],"mappings":"AAAA,OAAO,OAAO;AAEd,OAAO,2BAA2B;AAClC,SAAQ,WAAW,yBAAwB;AAE3C,SAAQ,4BAA2B;AAMnC,MAAO,6BAAmD,sBAAyB;AAAA,EAC/E,YACI,QACmB,KACA,QACrB;AACE,UAAM,MAAM;AAHO;AACA;AAAA,EAGvB;AAAA,EAJuB;AAAA,EACA;AAAA,EAKhB,OAAuB;AAC1B,WAAO,KAAK,OAAO,KAAK;AAAA,EAC5B;AAAA,EAEU,YAAiC;AACvC,WAAO,KAAK,OAAO,OAAO;AAAA,EAC9B;AAAA,EAEA,MAAgB,WAAyC;AACrD,UAAM,eAAe,MAAM,MAAM;AAAA,MAC7B,qBAAqB,KAAK,OAAO,SAAS,KAAK,KAAK;AAAA,QAChD,QAAQ,KAAK;AAAA,MACjB,CAAC;AAAA,IACL;AAEA,UAAM,QAAQ,oBAAI,IAAoB;AAEtC,aAAS,cAAc,GAAG,cAAc,aAAa,QAAQ,eAAe;AACxE,YAAM,EAAC,MAAM,OAAM,IAAI,aAAa,WAAW;AAC/C,UAAI,YAA4C,CAAC;AAEjD,UAAI,EAAE,UAAU,MAAM,GAAG;AACrB,oBAAY,CAAC,KAAK,GAAG;AAAA,MACzB,WAAW,EAAE,SAAS,MAAM,KAAK,EAAE,cAAc,MAAM,GAAG;AACtD,oBAAY,CAAC,MAAiC;AAAA,MAClD,WAAW,EAAE,QAAQ,MAAM,GAAG;AAC1B,oBAAY;AAAA,MAChB,WAAW,EAAE,MAAM,MAAM,GAAG;AACxB,oBAAY,MAAM,KAAK,MAA6B;AAAA,MACxD;AAEA,eAAS,WAAW,GAAG,WAAW,UAAU,QAAQ,YAAY;AAC5D,cAAM,WAAW,UAAU,QAAQ;AACnC,cAAM,OAAO,EAAE,SAAS,QAAQ,IAAI,KAAK,QAAQ,MAAM,QAAQ,IAAI;AAEnE,0BAAkB,MAAM;AAAA,UACpB,OAAO,UAAU;AAAA,UACjB,OAAO;AAAA,UACP;AAAA,QACJ,CAAC;AACD,cAAM,IAAI,IAAI;AAAA,MAClB;AAAA,IACJ;AAEA,WAAO;AAAA,EACX;AACJ;","names":[]}
@@ -5,7 +5,9 @@ type CspEntrypointOptions = ViewEntrypointOptions & {
5
5
  csp?: unknown;
6
6
  };
7
7
  export default abstract class<O extends CspEntrypointOptions, Csp = CspConfig> extends AbstractViewFinder<O> {
8
+ protected _csp?: Csp[];
8
9
  protected getViews(): Promise<ViewItems<O>>;
9
10
  csp(): Promise<Csp[]>;
11
+ clear(): this;
10
12
  }
11
13
  export {};
@@ -1,21 +1,28 @@
1
1
  import AbstractViewFinder from "./AbstractViewFinder.js";
2
2
  class ViewCspFinder_default extends AbstractViewFinder {
3
+ _csp;
3
4
  async getViews() {
4
5
  const views = await super.getViews();
6
+ const policies = [];
5
7
  for (const view of views.values()) {
6
8
  const { csp, ...options } = view.options;
9
+ if (csp) {
10
+ policies.push(csp);
11
+ }
7
12
  view.options = options;
8
13
  }
14
+ this._csp = policies;
9
15
  return views;
10
16
  }
11
17
  async csp() {
12
- const policies = [];
13
- for (const [, options] of await this.plugin().options()) {
14
- if (options.csp) {
15
- policies.push(options.csp);
16
- }
18
+ if (!this._csp) {
19
+ await this.views();
17
20
  }
18
- return policies;
21
+ return this._csp ?? [];
22
+ }
23
+ clear() {
24
+ this._csp = void 0;
25
+ return super.clear();
19
26
  }
20
27
  }
21
28
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/cli/entrypoint/finder/ViewCspFinder.ts"],"sourcesContent":["import AbstractViewFinder, {ViewItems} from \"./AbstractViewFinder\";\n\nimport type {CspConfig} from \"@typing/csp\";\nimport type {ViewEntrypointOptions} from \"@typing/view\";\n\ntype CspEntrypointOptions = ViewEntrypointOptions & {csp?: unknown};\n\nexport default abstract class<O extends CspEntrypointOptions, Csp = CspConfig> extends AbstractViewFinder<O> {\n protected async getViews(): Promise<ViewItems<O>> {\n const views = await super.getViews();\n\n for (const view of views.values()) {\n const {csp, ...options} = view.options;\n\n view.options = options as O;\n }\n\n return views;\n }\n\n public async csp(): Promise<Csp[]> {\n const policies: Csp[] = [];\n\n for (const [, options] of await this.plugin().options()) {\n if (options.csp) {\n policies.push(options.csp as Csp);\n }\n }\n\n return policies;\n }\n}\n"],"mappings":"AAAA,OAAO,wBAAqC;AAO5C,MAAO,8BAAgF,mBAAsB;AAAA,EACzG,MAAgB,WAAkC;AAC9C,UAAM,QAAQ,MAAM,MAAM,SAAS;AAEnC,eAAW,QAAQ,MAAM,OAAO,GAAG;AAC/B,YAAM,EAAC,KAAK,GAAG,QAAO,IAAI,KAAK;AAE/B,WAAK,UAAU;AAAA,IACnB;AAEA,WAAO;AAAA,EACX;AAAA,EAEA,MAAa,MAAsB;AAC/B,UAAM,WAAkB,CAAC;AAEzB,eAAW,CAAC,EAAE,OAAO,KAAK,MAAM,KAAK,OAAO,EAAE,QAAQ,GAAG;AACrD,UAAI,QAAQ,KAAK;AACb,iBAAS,KAAK,QAAQ,GAAU;AAAA,MACpC;AAAA,IACJ;AAEA,WAAO;AAAA,EACX;AACJ;","names":[]}
1
+ {"version":3,"sources":["../../../../src/cli/entrypoint/finder/ViewCspFinder.ts"],"sourcesContent":["import AbstractViewFinder, {ViewItems} from \"./AbstractViewFinder\";\n\nimport type {CspConfig} from \"@typing/csp\";\nimport type {ViewEntrypointOptions} from \"@typing/view\";\n\ntype CspEntrypointOptions = ViewEntrypointOptions & {csp?: unknown};\n\nexport default abstract class<O extends CspEntrypointOptions, Csp = CspConfig> extends AbstractViewFinder<O> {\n protected _csp?: Csp[];\n\n protected async getViews(): Promise<ViewItems<O>> {\n const views = await super.getViews();\n const policies: Csp[] = [];\n\n for (const view of views.values()) {\n const {csp, ...options} = view.options;\n\n if (csp) {\n policies.push(csp as Csp);\n }\n\n view.options = options as O;\n }\n\n this._csp = policies;\n\n return views;\n }\n\n public async csp(): Promise<Csp[]> {\n if (!this._csp) {\n await this.views();\n }\n\n return this._csp ?? [];\n }\n\n public clear(): this {\n this._csp = undefined;\n\n return super.clear();\n }\n}\n"],"mappings":"AAAA,OAAO,wBAAqC;AAO5C,MAAO,8BAAgF,mBAAsB;AAAA,EAC/F;AAAA,EAEV,MAAgB,WAAkC;AAC9C,UAAM,QAAQ,MAAM,MAAM,SAAS;AACnC,UAAM,WAAkB,CAAC;AAEzB,eAAW,QAAQ,MAAM,OAAO,GAAG;AAC/B,YAAM,EAAC,KAAK,GAAG,QAAO,IAAI,KAAK;AAE/B,UAAI,KAAK;AACL,iBAAS,KAAK,GAAU;AAAA,MAC5B;AAEA,WAAK,UAAU;AAAA,IACnB;AAEA,SAAK,OAAO;AAEZ,WAAO;AAAA,EACX;AAAA,EAEA,MAAa,MAAsB;AAC/B,QAAI,CAAC,KAAK,MAAM;AACZ,YAAM,KAAK,MAAM;AAAA,IACrB;AAEA,WAAO,KAAK,QAAQ,CAAC;AAAA,EACzB;AAAA,EAEO,QAAc;AACjB,SAAK,OAAO;AAEZ,WAAO,MAAM,MAAM;AAAA,EACvB;AACJ;","names":[]}
@@ -0,0 +1,35 @@
1
+ import { EntrypointFile } from "../../../../types/entrypoint.js";
2
+ /**
3
+ * Numeric values define the composition order from the least specific layer
4
+ * to the most specific one. Consumers that merge values therefore let later
5
+ * workspace layers override plugins, while singleton consumers select the
6
+ * final candidate.
7
+ */
8
+ export declare enum FileLayer {
9
+ Plugin = 0,
10
+ Source = 1,
11
+ Shared = 2,
12
+ App = 3,
13
+ AppSource = 4
14
+ }
15
+ export type WorkspaceFileLayer = Exclude<FileLayer, FileLayer.Plugin>;
16
+ export declare enum FileSpecificity {
17
+ Generic = 0,
18
+ Browser = 1
19
+ }
20
+ export interface FilePrecedence {
21
+ layer: FileLayer;
22
+ order?: number;
23
+ sequence?: number;
24
+ specificity?: FileSpecificity;
25
+ }
26
+ /**
27
+ * Workspace discovery runs from highest to lowest priority. This lets
28
+ * non-merge consumers stop at the first non-empty layer and ensures that
29
+ * overlapping canonical paths keep their most specific layer.
30
+ */
31
+ export declare const getWorkspaceFileLayers: () => readonly WorkspaceFileLayer[];
32
+ export declare const setFilePrecedence: (file: EntrypointFile, value: FilePrecedence) => EntrypointFile;
33
+ export declare const setFileSpecificity: (file: EntrypointFile, specificity: FileSpecificity) => EntrypointFile;
34
+ export declare const getFilePrecedence: (file: EntrypointFile) => Required<FilePrecedence>;
35
+ export declare const compareFilePrecedence: (a: EntrypointFile, b: EntrypointFile) => number;
@@ -0,0 +1,64 @@
1
+ var FileLayer = /* @__PURE__ */ ((FileLayer2) => {
2
+ FileLayer2[FileLayer2["Plugin"] = 0] = "Plugin";
3
+ FileLayer2[FileLayer2["Source"] = 1] = "Source";
4
+ FileLayer2[FileLayer2["Shared"] = 2] = "Shared";
5
+ FileLayer2[FileLayer2["App"] = 3] = "App";
6
+ FileLayer2[FileLayer2["AppSource"] = 4] = "AppSource";
7
+ return FileLayer2;
8
+ })(FileLayer || {});
9
+ var FileSpecificity = /* @__PURE__ */ ((FileSpecificity2) => {
10
+ FileSpecificity2[FileSpecificity2["Generic"] = 0] = "Generic";
11
+ FileSpecificity2[FileSpecificity2["Browser"] = 1] = "Browser";
12
+ return FileSpecificity2;
13
+ })(FileSpecificity || {});
14
+ const precedence = /* @__PURE__ */ new WeakMap();
15
+ const normalize = ({
16
+ layer,
17
+ order = 0,
18
+ sequence = 0,
19
+ specificity = 0 /* Generic */
20
+ }) => ({
21
+ layer,
22
+ order,
23
+ sequence,
24
+ specificity
25
+ });
26
+ const fallback = normalize({ layer: 0 /* Plugin */ });
27
+ const isWorkspaceFileLayer = (layer) => {
28
+ return typeof layer === "number" && layer !== 0 /* Plugin */;
29
+ };
30
+ const workspaceFileLayers = Object.freeze(
31
+ Object.values(FileLayer).filter(isWorkspaceFileLayer).sort((a, b) => b - a)
32
+ );
33
+ const getWorkspaceFileLayers = () => workspaceFileLayers;
34
+ const setFilePrecedence = (file, value) => {
35
+ if (!precedence.has(file)) {
36
+ precedence.set(file, normalize(value));
37
+ }
38
+ return file;
39
+ };
40
+ const setFileSpecificity = (file, specificity) => {
41
+ precedence.set(file, {
42
+ ...precedence.get(file) ?? fallback,
43
+ specificity
44
+ });
45
+ return file;
46
+ };
47
+ const getFilePrecedence = (file) => {
48
+ return precedence.get(file) ?? fallback;
49
+ };
50
+ const compareFilePrecedence = (a, b) => {
51
+ const left = getFilePrecedence(a);
52
+ const right = getFilePrecedence(b);
53
+ return left.layer - right.layer || left.order - right.order || left.specificity - right.specificity || left.sequence - right.sequence;
54
+ };
55
+ export {
56
+ FileLayer,
57
+ FileSpecificity,
58
+ compareFilePrecedence,
59
+ getFilePrecedence,
60
+ getWorkspaceFileLayers,
61
+ setFilePrecedence,
62
+ setFileSpecificity
63
+ };
64
+ //# sourceMappingURL=filePrecedence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../src/cli/entrypoint/finder/utils/filePrecedence.ts"],"sourcesContent":["import {EntrypointFile} from \"@typing/entrypoint\";\n\n/**\n * Numeric values define the composition order from the least specific layer\n * to the most specific one. Consumers that merge values therefore let later\n * workspace layers override plugins, while singleton consumers select the\n * final candidate.\n */\nexport enum FileLayer {\n Plugin,\n Source,\n Shared,\n App,\n AppSource,\n}\n\nexport type WorkspaceFileLayer = Exclude<FileLayer, FileLayer.Plugin>;\n\nexport enum FileSpecificity {\n Generic,\n Browser,\n}\n\nexport interface FilePrecedence {\n layer: FileLayer;\n order?: number;\n sequence?: number;\n specificity?: FileSpecificity;\n}\n\nconst precedence = new WeakMap<EntrypointFile, Required<FilePrecedence>>();\n\nconst normalize = ({\n layer,\n order = 0,\n sequence = 0,\n specificity = FileSpecificity.Generic,\n}: FilePrecedence): Required<FilePrecedence> => ({\n layer,\n order,\n sequence,\n specificity,\n});\n\nconst fallback = normalize({layer: FileLayer.Plugin});\n\nconst isWorkspaceFileLayer = (layer: string | FileLayer): layer is WorkspaceFileLayer => {\n return typeof layer === \"number\" && layer !== FileLayer.Plugin;\n};\n\nconst workspaceFileLayers = Object.freeze(\n Object.values(FileLayer)\n .filter(isWorkspaceFileLayer)\n .sort((a, b) => b - a)\n);\n\n/**\n * Workspace discovery runs from highest to lowest priority. This lets\n * non-merge consumers stop at the first non-empty layer and ensures that\n * overlapping canonical paths keep their most specific layer.\n */\nexport const getWorkspaceFileLayers = (): readonly WorkspaceFileLayer[] => workspaceFileLayers;\n\nexport const setFilePrecedence = (file: EntrypointFile, value: FilePrecedence): EntrypointFile => {\n if (!precedence.has(file)) {\n precedence.set(file, normalize(value));\n }\n\n return file;\n};\n\nexport const setFileSpecificity = (file: EntrypointFile, specificity: FileSpecificity): EntrypointFile => {\n precedence.set(file, {\n ...(precedence.get(file) ?? fallback),\n specificity,\n });\n\n return file;\n};\n\nexport const getFilePrecedence = (file: EntrypointFile): Required<FilePrecedence> => {\n return precedence.get(file) ?? fallback;\n};\n\nexport const compareFilePrecedence = (a: EntrypointFile, b: EntrypointFile): number => {\n const left = getFilePrecedence(a);\n const right = getFilePrecedence(b);\n\n return (\n left.layer - right.layer ||\n left.order - right.order ||\n left.specificity - right.specificity ||\n left.sequence - right.sequence\n );\n};\n"],"mappings":"AAQO,IAAK,YAAL,kBAAKA,eAAL;AACH,EAAAA,sBAAA;AACA,EAAAA,sBAAA;AACA,EAAAA,sBAAA;AACA,EAAAA,sBAAA;AACA,EAAAA,sBAAA;AALQ,SAAAA;AAAA,GAAA;AAUL,IAAK,kBAAL,kBAAKC,qBAAL;AACH,EAAAA,kCAAA;AACA,EAAAA,kCAAA;AAFQ,SAAAA;AAAA,GAAA;AAYZ,MAAM,aAAa,oBAAI,QAAkD;AAEzE,MAAM,YAAY,CAAC;AAAA,EACf;AAAA,EACA,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,cAAc;AAClB,OAAiD;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ;AAEA,MAAM,WAAW,UAAU,EAAC,OAAO,eAAgB,CAAC;AAEpD,MAAM,uBAAuB,CAAC,UAA2D;AACrF,SAAO,OAAO,UAAU,YAAY,UAAU;AAClD;AAEA,MAAM,sBAAsB,OAAO;AAAA,EAC/B,OAAO,OAAO,SAAS,EAClB,OAAO,oBAAoB,EAC3B,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAC7B;AAOO,MAAM,yBAAyB,MAAqC;AAEpE,MAAM,oBAAoB,CAAC,MAAsB,UAA0C;AAC9F,MAAI,CAAC,WAAW,IAAI,IAAI,GAAG;AACvB,eAAW,IAAI,MAAM,UAAU,KAAK,CAAC;AAAA,EACzC;AAEA,SAAO;AACX;AAEO,MAAM,qBAAqB,CAAC,MAAsB,gBAAiD;AACtG,aAAW,IAAI,MAAM;AAAA,IACjB,GAAI,WAAW,IAAI,IAAI,KAAK;AAAA,IAC5B;AAAA,EACJ,CAAC;AAED,SAAO;AACX;AAEO,MAAM,oBAAoB,CAAC,SAAmD;AACjF,SAAO,WAAW,IAAI,IAAI,KAAK;AACnC;AAEO,MAAM,wBAAwB,CAAC,GAAmB,MAA8B;AACnF,QAAM,OAAO,kBAAkB,CAAC;AAChC,QAAM,QAAQ,kBAAkB,CAAC;AAEjC,SACI,KAAK,QAAQ,MAAM,SACnB,KAAK,QAAQ,MAAM,SACnB,KAAK,cAAc,MAAM,eACzB,KAAK,WAAW,MAAM;AAE9B;","names":["FileLayer","FileSpecificity"]}
@@ -421,6 +421,14 @@ export interface Config {
421
421
  * Flag indicating whether to merge localizations from App and Shared directories.
422
422
  * When `true`, localization files from both directories will be combined.
423
423
  *
424
+ * Locale values are resolved from the least specific source to the most specific:
425
+ * plugins, source root, Shared, and App. Within each layer, a browser-specific
426
+ * file overrides its generic file. A value from a later source overrides the
427
+ * same key from an earlier source. When `false`, plugin locales remain available
428
+ * as a baseline and only the highest available workspace layer is selected.
429
+ * Multiple generic or browser-specific files for the same language in one layer
430
+ * are rejected as ambiguous.
431
+ *
424
432
  * @default true
425
433
  */
426
434
  mergeLocales: boolean;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "adnbn",
3
3
  "type": "module",
4
- "version": "0.7.1",
4
+ "version": "0.8.0",
5
5
  "description": "Addon Bone - Cross-browser web extension framework with shared code base",
6
6
  "homepage": "https://addonbone.com",
7
7
  "author": "Anjey Tsibylskij (https://github.com/atldays)",