adnbn 0.7.0 → 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.
- package/README.md +27 -47
- package/dist/cli/entrypoint/finder/AbstractAssetFinder.js +22 -11
- package/dist/cli/entrypoint/finder/AbstractAssetFinder.js.map +1 -1
- package/dist/cli/entrypoint/finder/AbstractEntrypointFinder.js +7 -0
- package/dist/cli/entrypoint/finder/AbstractEntrypointFinder.js.map +1 -1
- package/dist/cli/entrypoint/finder/AbstractFinder.d.ts +0 -2
- package/dist/cli/entrypoint/finder/AbstractFinder.js +5 -25
- package/dist/cli/entrypoint/finder/AbstractFinder.js.map +1 -1
- package/dist/cli/entrypoint/finder/AbstractViewFinder.js +3 -4
- package/dist/cli/entrypoint/finder/AbstractViewFinder.js.map +1 -1
- package/dist/cli/entrypoint/finder/AssetPluginFinder.js +14 -1
- package/dist/cli/entrypoint/finder/AssetPluginFinder.js.map +1 -1
- package/dist/cli/entrypoint/finder/LocaleFinder.d.ts +2 -0
- package/dist/cli/entrypoint/finder/LocaleFinder.js +27 -0
- package/dist/cli/entrypoint/finder/LocaleFinder.js.map +1 -1
- package/dist/cli/entrypoint/finder/PluginFinder.js +12 -3
- package/dist/cli/entrypoint/finder/PluginFinder.js.map +1 -1
- package/dist/cli/entrypoint/finder/ViewCspFinder.d.ts +2 -0
- package/dist/cli/entrypoint/finder/ViewCspFinder.js +13 -6
- package/dist/cli/entrypoint/finder/ViewCspFinder.js.map +1 -1
- package/dist/cli/entrypoint/finder/utils/filePrecedence.d.ts +35 -0
- package/dist/cli/entrypoint/finder/utils/filePrecedence.js +64 -0
- package/dist/cli/entrypoint/finder/utils/filePrecedence.js.map +1 -0
- package/dist/types/config.d.ts +8 -0
- package/package.json +9 -6
package/README.md
CHANGED
|
@@ -1,60 +1,40 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Addon Bone
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[](https://www.npmjs.com/package/adnbn)
|
|
3
|
+
> **🚧 Active development** — the public API and documentation may change between releases. Full guides and API reference are being prepared at [addonbone.com](https://addonbone.com).
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/adnbn)
|
|
6
|
+
[](https://www.npmjs.com/package/adnbn)
|
|
7
|
+
[](https://github.com/addon-stack/addon-bone/actions/workflows/ci.yml)
|
|
8
|
+
[](LICENSE.md)
|
|
7
9
|
|
|
8
|
-
|
|
10
|
+
A TypeScript framework for building browser extensions from a shared codebase.
|
|
11
|
+
Define extension entrypoints in source files; Addon Bone discovers them and creates
|
|
12
|
+
the browser-specific build and manifest.
|
|
9
13
|
|
|
10
|
-
|
|
14
|
+
## Install
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
```bash
|
|
17
|
+
npm i -D adnbn
|
|
18
|
+
```
|
|
15
19
|
|
|
16
|
-
|
|
20
|
+
## Get started
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
- **Manifest Versions**: Manifest V2 and V3.
|
|
22
|
+
Create a project, then build it for production:
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
```bash
|
|
25
|
+
npx adnbn init
|
|
26
|
+
npx adnbn build
|
|
27
|
+
```
|
|
22
28
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
- **Commands**
|
|
26
|
-
- **Localization**
|
|
27
|
-
- **Icons**
|
|
28
|
-
- **Messages**
|
|
29
|
-
- **Services**
|
|
30
|
-
- **Relay**
|
|
31
|
-
- **Offscreen**
|
|
32
|
-
- **Popup**
|
|
33
|
-
- **Sidebar**
|
|
29
|
+
For project structure, entrypoint guides, and the API reference, visit
|
|
30
|
+
[addonbone.com](https://addonbone.com).
|
|
34
31
|
|
|
35
|
-
|
|
32
|
+
## Links
|
|
36
33
|
|
|
37
|
-
-
|
|
38
|
-
-
|
|
34
|
+
- [Documentation](https://addonbone.com)
|
|
35
|
+
- [Contributing](CONTRIBUTING.md)
|
|
36
|
+
- [Issues](https://github.com/addon-stack/addon-bone/issues)
|
|
39
37
|
|
|
40
|
-
|
|
38
|
+
## License
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
- **Relay**: similar mechanism for content pages, bypassing CSP and getUserGesture restrictions by leveraging scripting contexts.
|
|
44
|
-
|
|
45
|
-
### Offscreen
|
|
46
|
-
|
|
47
|
-
- **Manifest V2**: offscreen support via `<iframe>` within the Background Page.
|
|
48
|
-
- **Manifest V3**: native Offscreen API.
|
|
49
|
-
|
|
50
|
-
### Benefits
|
|
51
|
-
|
|
52
|
-
- 🔌 **Plugin Architecture**: easily extend functionality and add features without modifying the core framework.
|
|
53
|
-
- 🔧 **Faster Development**: minimal configuration and boilerplate.
|
|
54
|
-
- 🌐 **Cross-Platform**: write code once; run everywhere regardless of browser or manifest version.
|
|
55
|
-
- 🔄 **Scalable**: seamlessly add new layers and entry points.
|
|
56
|
-
- 🚀 **Modern Design**: cutting-edge architecture following best practices for extension publishing.
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
> _This framework is under active development. Detailed documentation and usage examples will be available soon on the official website._
|
|
40
|
+
[MIT](LICENSE.md)
|
|
@@ -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
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
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":"AAAA,OAAO,QAAkB;AACzB,OAAO,UAAU;AACjB,OAAO,eAAe;AAEtB,OAAO,2BAA2B;
|
|
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":"AAAA,OAAO,QAAkB;AACzB,OAAO,UAAU;AACjB,OAAO,eAAe;AAEtB,OAAO,2BAA2B;AAClC,SAAQ,WAAW,yBAAwB;AAE3C,SAAQ,kBAAkB,qBAAoB;AAE9C,SAAwB,gCAAkD;AAG1E,MAAO,yCAA4D,sBAAyB;AAAA,EAC9E;AAAA,EAEA;AAAA,EAEA,YAAY,QAAwB;AAC1C,UAAM,MAAM;AAEZ,SAAK,wBAAwB,CAAC,GAAG,wBAAwB,EAAE,IAAI,SAAO,IAAI,QAAQ,KAAK,KAAK,CAAC,EAAE,KAAK,GAAG;AAEvG,SAAK,qBAAqB,IAAI,IAAI,CAAC,GAAG,wBAAwB,EAAE,IAAI,SAAO,SAAS,GAAG,EAAE,CAAC;AAAA,EAC9F;AAAA,EAEO,WAAoB;AACvB,WAAO;AAAA,EACX;AAAA,EAEA,MAAgB,WAAyC;AACrD,UAAM,aAAa,KAAK,KAAK;AAE7B,QAAI,QAAQ,oBAAI,IAAoB;AAEpC,UAAM,WAAW,KAAK,UAAU,iBAAiB,KAAK,MAAM,CAAC;AAE7D,eAAW,QAAQ,UAAU;AACzB,wBAAkB,MAAM,EAAC,OAAO,UAAU,UAAS,CAAC;AAAA,IACxD;AAEA,QAAI,SAAS,OAAO,GAAG;AACnB,cAAQ;AAER,UAAI,KAAK,OAAO,OAAO;AACnB,gBAAQ,KAAK,QAAQ,UAAU,kBAAkB,QAAQ;AAAA,MAC7D;AAAA,IACJ;AAEA,QAAK,SAAS,OAAO,KAAK,KAAK,SAAS,KAAM,SAAS,SAAS,GAAG;AAC/D,YAAM,cAAc,KAAK,UAAU,cAAc,KAAK,MAAM,CAAC;AAE7D,iBAAW,QAAQ,aAAa;AAC5B,0BAAkB,MAAM,EAAC,OAAO,UAAU,OAAM,CAAC;AAAA,MACrD;AAEA,UAAI,YAAY,OAAO,GAAG;AACtB,gBAAQ,oBAAI,IAAI,CAAC,GAAG,OAAO,GAAG,WAAW,CAAC;AAE1C,YAAI,KAAK,OAAO,OAAO;AACnB,kBAAQ,KAAK,WAAW,UAAU,kBAAkB,WAAW;AAAA,QACnE;AAAA,MACJ;AAAA,IACJ;AAEA,WAAO;AAAA,EACX;AAAA,EAEU,UAAU,WAAwC;AACxD,UAAM,aAAa,KAAK,KAAK;AAC7B,UAAM,sBAAsB,UAAU,UAAU;AAEhD,UAAM,YAA8B,CAAC;AACrC,UAAM,eAAiC,CAAC;AAExC,UAAM,UAAU,CAAC,KAAa,OAAyB,mBAAkC;AACrF,UAAI;AAEJ,UAAI;AACA,kBAAU,GAAG,YAAY,KAAK,EAAC,eAAe,KAAI,CAAC;AAAA,MACvD,SAAS,GAAG;AACR,YAAI,KAAK,OAAO,OAAO;AACnB,kBAAQ,IAAI,kBAAkB,UAAU,2BAA2B,GAAG,cAAc;AAAA,QACxF;AAEA;AAAA,MACJ;AAEA,iBAAW,SAAS,SAAS;AACzB,cAAM,WAAW,KAAK,KAAK,KAAK,MAAM,IAAI;AAE1C,YAAI,MAAM,YAAY,GAAG;AACrB,cAAI,KAAK,eAAe,MAAM,IAAI,GAAG;AACjC,uBAAW,aAAa,KAAK,oBAAoB;AAC7C,oBAAM,YAAY,KAAK,KAAK,UAAU,SAAS;AAE/C,kBAAI;AACA,sBAAM,OAAO,GAAG,SAAS,SAAS;AAElC,oBAAI,KAAK,OAAO,GAAG;AACf,wBAAM,KAAK,KAAK,KAAK,SAAS,CAAC;AAAA,gBACnC;AAAA,cACJ,SAAS,GAAG;AACR,oBAAI,KAAK,OAAO,OAAO;AACnB,0BAAQ;AAAA,oBACJ,iBAAiB,UAAU,4BAA4B,SAAS;AAAA,kBACpE;AAAA,gBACJ;AAAA,cACJ;AAAA,YACJ;AAAA,UACJ,WAAW,kBAAkB,MAAM,SAAS,qBAAqB;AAC7D,oBAAQ,UAAU,cAAc,KAAK;AAAA,UACzC;AAAA,QACJ,WAAW,MAAM,OAAO,KAAK,KAAK,gBAAgB,MAAM,IAAI,GAAG;AAC3D,gBAAM,KAAK,KAAK,KAAK,QAAQ,CAAC;AAAA,QAClC;AAAA,MACJ;AAAA,IACJ;AAEA,YAAQ,WAAW,WAAW,IAAI;AAElC,QAAI,aAAa,SAAS,GAAG;AACzB,aAAO,IAAI,IAAI,YAAY;AAAA,IAC/B;AAEA,QAAI,UAAU,SAAS,GAAG;AACtB,aAAO,IAAI,IAAI,SAAS;AAAA,IAC5B;AAEA,QAAI;AACA,kBAAY,KAAK,KAAK,WAAW,UAAU;AAE3C,YAAM,OAAO,GAAG,SAAS,SAAS;AAElC,UAAI,KAAK,YAAY,GAAG;AACpB,gBAAQ,WAAW,WAAW,KAAK;AAAA,MACvC;AAAA,IACJ,SAAS,GAAG;AACR,UAAI,KAAK,OAAO,OAAO;AACnB,gBAAQ,IAAI,uCAAuC,SAAS;AAAA,MAChE;AAAA,IACJ;AAEA,WAAO,IAAI,IAAI,SAAS;AAAA,EAC5B;AAAA,EAEU,gBAAgB,UAA2B;AACjD,UAAM,OAAO,KAAK,KAAK,EAAE,QAAQ,uBAAuB,MAAM;AAE9D,UAAM,UAAU,IAAI,OAAO,cAAc,IAAI,OAAO,KAAK,qBAAqB,IAAI;AAElF,WAAO,QAAQ,KAAK,QAAQ;AAAA,EAChC;AAAA,EAEU,eAAe,SAA0B;AAC/C,UAAM,aAAa,KAAK,KAAK;AAE7B,WAAO,YAAY,cAAc,QAAQ,SAAS,IAAI,UAAU,EAAE;AAAA,EACtE;AACJ;","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
|
|
42
|
-
|
|
43
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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":"AAAA,OAAO,OAAO;AACd,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,OAAO,UAAU;AAEjB,OAAO,yBAAyB;AAChC,OAAO,uBAAuB;
|
|
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 (
|
|
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 (
|
|
37
|
-
|
|
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}
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
if (options.csp) {
|
|
15
|
-
policies.push(options.csp);
|
|
16
|
-
}
|
|
18
|
+
if (!this._csp) {
|
|
19
|
+
await this.views();
|
|
17
20
|
}
|
|
18
|
-
return
|
|
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
|
|
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"]}
|
package/dist/types/config.d.ts
CHANGED
|
@@ -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.
|
|
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)",
|
|
@@ -109,10 +109,12 @@
|
|
|
109
109
|
"format": "prettier --write .",
|
|
110
110
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
111
111
|
"check:node-version": "node ./scripts/check-node-version.js",
|
|
112
|
-
"test": "jest",
|
|
113
|
-
"test:ci": "
|
|
114
|
-
"test:
|
|
112
|
+
"test": "npm run build && jest",
|
|
113
|
+
"test:ci": "npm run test -- --ci --passWithNoTests --coverage",
|
|
114
|
+
"test:ci:non-browser": "npm run test -- --ci --passWithNoTests --coverage --testPathIgnorePatterns=tests/integration/browser",
|
|
115
|
+
"test:watch": "jest --watch --testPathIgnorePatterns=tests/integration/browser",
|
|
115
116
|
"test:offscreen": "jest --testPathPatterns=Offscreen",
|
|
117
|
+
"test:chrome": "npm run test -- --testPathPatterns=tests/integration/browser --runInBand",
|
|
116
118
|
"test:message": "jest --testPathPatterns=src/message",
|
|
117
119
|
"test:service": "jest --testPathPatterns=Service",
|
|
118
120
|
"test:relay": "jest --testPathPatterns=Relay",
|
|
@@ -124,9 +126,9 @@
|
|
|
124
126
|
"release:preview": "release-it --no-github.release --no-npm.publish --no-git.tag --ci"
|
|
125
127
|
},
|
|
126
128
|
"dependencies": {
|
|
127
|
-
"@addon-core/browser": "^0.
|
|
129
|
+
"@addon-core/browser": "^0.7.1",
|
|
128
130
|
"@addon-core/inject-script": "^0.3.1",
|
|
129
|
-
"@addon-core/storage": "^0.
|
|
131
|
+
"@addon-core/storage": "^0.7.0",
|
|
130
132
|
"@rsdoctor/rspack-plugin": "^1.5.1",
|
|
131
133
|
"@rspack/cli": "^1.7.5",
|
|
132
134
|
"@rspack/core": "^1.7.5",
|
|
@@ -177,6 +179,7 @@
|
|
|
177
179
|
"@types/react": "^19.1.10",
|
|
178
180
|
"@types/react-dom": "^19.1.7",
|
|
179
181
|
"@types/semver": "^7.7.1",
|
|
182
|
+
"chrome-launcher": "^1.2.1",
|
|
180
183
|
"cross-env": "^7.0.3",
|
|
181
184
|
"dotenv": "^16.4.7",
|
|
182
185
|
"esbuild": "^0.25.10",
|