paralayer 1.3.0 → 1.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paralayer",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "imkost",
@@ -10,26 +10,20 @@
10
10
  "vite-plugin"
11
11
  ],
12
12
  "scripts": {
13
- "build": "tsup src --format esm --dts --clean --no-splitting",
14
13
  "lint": "tsc --noEmit",
15
- "release": "sh -c 'npm version ${1:-minor} && npm run build && npm publish' --"
14
+ "release": "sh -c 'npm version ${1:-minor} && npm publish' --"
16
15
  },
17
16
  "bin": {
18
- "paralayer": "./dist/bin.js",
19
- "paralayer-ts": "./src/bin.ts"
17
+ "paralayer": "./src/bin.ts"
20
18
  },
21
19
  "exports": {
22
- ".": {
23
- "source": "./src/paralayer.ts",
24
- "import": "./dist/paralayer.js"
25
- }
20
+ ".": "./src/paralayer.ts"
26
21
  },
27
22
  "files": [
28
- "src",
29
- "dist"
23
+ "src"
30
24
  ],
31
25
  "dependencies": {
32
- "@eposlabs/utils": "^1.2.2",
26
+ "@eposlabs/utils": "^1.5.0",
33
27
  "chokidar": "^4.0.3",
34
28
  "minimist": "^1.2.8"
35
29
  },
package/src/paralayer.ts CHANGED
@@ -204,7 +204,7 @@ export class Paralayer extends Unit {
204
204
  `}`,
205
205
  ]
206
206
 
207
- return [...imports, '', ...assign, '', ...globals, ''].join('\n')
207
+ return ['// @ts-ignore', '', ...imports, '', ...assign, '', ...globals, ''].join('\n')
208
208
  }
209
209
 
210
210
  private generateIndexContent(topLayer: string, allLayers: string[]) {
package/dist/bin.d.ts DELETED
@@ -1 +0,0 @@
1
- #!/usr/bin/env node
package/dist/bin.js DELETED
@@ -1,218 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/bin.ts
4
- import minimist from "minimist";
5
-
6
- // src/paralayer.ts
7
- import { Queue, safe, Unit } from "@eposlabs/utils";
8
- import { watch } from "chokidar";
9
- import { mkdir, readFile, rm, writeFile } from "fs/promises";
10
- import { basename, extname, join, relative } from "path";
11
- var Paralayer = class extends Unit {
12
- files = {};
13
- options;
14
- started = false;
15
- ready = false;
16
- ready$ = Promise.withResolvers();
17
- queue = new Queue();
18
- extensions = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
19
- previousLayers = [];
20
- watcher = null;
21
- constructor(options) {
22
- super();
23
- this.options = options;
24
- }
25
- async start() {
26
- if (this.started) return;
27
- this.started = true;
28
- await safe(rm(this.options.output, { recursive: true }));
29
- this.watcher = watch(this.options.input);
30
- this.watcher.on("all", this.onAll);
31
- this.watcher.on("ready", this.onReady);
32
- await this.ready$.promise;
33
- await this.queue.run(() => this.build());
34
- if (!this.options.watch) await this.watcher.close();
35
- }
36
- async readSetupJs() {
37
- const setupJsPath = join(this.options.output, "setup.js");
38
- return await readFile(setupJsPath, "utf-8");
39
- }
40
- // ---------------------------------------------------------------------------
41
- // HANDLERS
42
- // ---------------------------------------------------------------------------
43
- onAll = async (event, path) => {
44
- if (!["add", "change", "unlink"].includes(event)) return;
45
- const ext = extname(path);
46
- if (!this.extensions.has(ext)) return;
47
- if (!this.getLayer(path)) return;
48
- if (!this.ready) {
49
- this.files[path] = null;
50
- return;
51
- }
52
- if (event === "unlink") {
53
- delete this.files[path];
54
- await this.queue.run(() => this.build());
55
- }
56
- if (event === "add" || event === "change") {
57
- this.files[path] = null;
58
- await this.queue.run(() => this.build());
59
- }
60
- };
61
- onReady = async () => {
62
- this.ready = true;
63
- this.ready$.resolve();
64
- };
65
- // ---------------------------------------------------------------------------
66
- // BUILD
67
- // ---------------------------------------------------------------------------
68
- async build() {
69
- const paths2 = Object.keys(this.files);
70
- if (paths2.length === 0) return;
71
- const pathsByLayers = Object.groupBy(paths2, (path) => this.getLayer(path));
72
- const allLayers = Object.keys(pathsByLayers);
73
- await mkdir(this.options.output, { recursive: true });
74
- await Promise.all(
75
- paths2.map(async (path) => {
76
- if (this.files[path]) return;
77
- const content = await readFile(path, "utf-8");
78
- const names = this.extractExportedClassNames(content);
79
- this.files[path] = { content, names };
80
- })
81
- );
82
- for (const layer in pathsByLayers) {
83
- const layerFile = join(this.options.output, `layer.${layer}.ts`);
84
- const layerPaths = pathsByLayers[layer].toSorted();
85
- const layerContent = this.generateLayerContent(layer, layerPaths);
86
- await this.write(layerFile, layerContent);
87
- if (this.isTopLayer(layer)) {
88
- const indexFile = join(this.options.output, `index.${layer}.ts`);
89
- const indexContent = this.generateIndexContent(layer, allLayers);
90
- await this.write(indexFile, indexContent);
91
- }
92
- }
93
- const removedLayers = this.previousLayers.filter((l) => !allLayers.includes(l));
94
- this.previousLayers = allLayers;
95
- for (const layer of removedLayers) {
96
- const layerFile = join(this.options.output, `layer.${layer}.ts`);
97
- await rm(layerFile);
98
- if (this.isTopLayer(layer)) {
99
- const indexFile = join(this.options.output, `index.${layer}.ts`);
100
- await rm(indexFile);
101
- }
102
- }
103
- const setupFile = join(this.options.output, "setup.js");
104
- const setupContent = this.generateSetupContent(allLayers);
105
- await this.write(setupFile, setupContent);
106
- }
107
- // ---------------------------------------------------------------------------
108
- // HELPERS
109
- // ---------------------------------------------------------------------------
110
- extractExportedClassNames(content) {
111
- if (content.includes("paralayer-ignore")) return [];
112
- return content.split("export class ").slice(1).map((part) => part.split(" ")[0].split("<")[0]);
113
- }
114
- generateLayerContent(layer, layerPaths) {
115
- const $LayerName = this.getLayerName(layer, "$Pascal");
116
- const $layerName = this.getLayerName(layer, "$camel");
117
- const allNames = layerPaths.flatMap((path) => this.files[path]?.names ?? []);
118
- const imports = layerPaths.map((path) => {
119
- const file = this.files[path];
120
- if (!file) return "";
121
- if (file.names.length === 0) return "";
122
- const names = file.names;
123
- const types = file.names.map((name) => `type ${name} as ${name}Type`);
124
- const relativePath = relative(this.options.output, path);
125
- return `import { ${[...names, ...types].join(", ")} } from '${relativePath}'`;
126
- }).filter(Boolean);
127
- const assign = [`Object.assign(${$layerName}, {`, ...allNames.map((name) => ` ${name},`), `})`];
128
- const globals = [
129
- `declare global {`,
130
- ` var ${$layerName}: ${$LayerName}`,
131
- ``,
132
- ` interface ${$LayerName} {`,
133
- ...allNames.map((name) => ` ${name}: typeof ${name}`),
134
- ` }`,
135
- ``,
136
- ` namespace ${$layerName} {`,
137
- ...allNames.map((name) => ` export type ${name} = ${name}Type`),
138
- ` }`,
139
- `}`
140
- ];
141
- return [...imports, "", ...assign, "", ...globals, ""].join("\n");
142
- }
143
- generateIndexContent(topLayer, allLayers) {
144
- const imports = allLayers.filter((layer) => layer.includes(topLayer)).sort((layer1, layer2) => {
145
- if (layer1.length !== layer2.length) return layer2.length - layer1.length;
146
- return layer1.localeCompare(layer2);
147
- }).map((layer) => `import './layer.${layer}.ts'`);
148
- return [...imports].join("\n");
149
- }
150
- generateSetupContent(allLayers) {
151
- const layers = allLayers.toSorted((layer1, layer2) => {
152
- if (layer1.length !== layer2.length) return layer1.length - layer2.length;
153
- return layer1.localeCompare(layer2);
154
- });
155
- const vars = layers.map((layer) => {
156
- const $layerName = this.getLayerName(layer, "$camel");
157
- if (this.options.globalize) return `globalThis.${$layerName} = {}`;
158
- return `const ${$layerName} = {}`;
159
- });
160
- return [...vars].join("\n");
161
- }
162
- getLayer(path) {
163
- const name = basename(path);
164
- const layer = name.split(".").slice(1, -1).sort().join(".");
165
- if (layer) return layer;
166
- return this.options.defaultLayerName ?? "";
167
- }
168
- getLayerName(layer, style) {
169
- const LayerName = layer.split(".").map(this.capitalize).join("");
170
- if (style === "$camel") return `$${this.decapitalize(LayerName)}`;
171
- if (style === "$Pascal") return `$${LayerName}`;
172
- throw this.never;
173
- }
174
- capitalize(string) {
175
- return string.charAt(0).toUpperCase() + string.slice(1);
176
- }
177
- decapitalize(string) {
178
- return string.charAt(0).toLowerCase() + string.slice(1);
179
- }
180
- isTopLayer(layer) {
181
- return !layer.includes(".");
182
- }
183
- async write(path, content) {
184
- const [prevContent] = await safe(() => readFile(path, "utf-8"));
185
- if (content === prevContent) return;
186
- await writeFile(path, content, "utf-8");
187
- }
188
- };
189
- async function paralayer(options) {
190
- const pl = new Paralayer(options);
191
- await pl.start();
192
- return await pl.readSetupJs();
193
- }
194
-
195
- // src/bin.ts
196
- var argv = minimist(process.argv.slice(2), {
197
- string: ["defaultLayerName"],
198
- boolean: ["watch", "globalize"],
199
- default: { watch: false, globalize: false, defaultLayerName: null }
200
- });
201
- var paths = argv._;
202
- var input = paths.slice(0, -1);
203
- var output = paths.at(-1);
204
- if (input.length === 0) {
205
- console.error("[paralayer] Input directory is not provided");
206
- process.exit(1);
207
- }
208
- if (!output) {
209
- console.error("[paralayer] Output directory is not provided");
210
- process.exit(1);
211
- }
212
- await paralayer({
213
- input,
214
- output,
215
- watch: argv.watch,
216
- globalize: argv.globalize,
217
- defaultLayerName: argv.defaultLayerName
218
- });
@@ -1,46 +0,0 @@
1
- import { Unit } from '@eposlabs/utils';
2
-
3
- type DirPath = string;
4
- type File = {
5
- content: string;
6
- names: string[];
7
- };
8
- type Options = {
9
- input: DirPath | DirPath[];
10
- output: DirPath;
11
- watch?: boolean;
12
- /** Whether the layer variables should be exposed globally. */
13
- globalize?: boolean;
14
- /** If a file name does not have layer tags, default layer name will be used. */
15
- defaultLayerName?: string | null;
16
- };
17
- declare class Paralayer extends Unit {
18
- private files;
19
- private options;
20
- private started;
21
- private ready;
22
- private ready$;
23
- private queue;
24
- private extensions;
25
- private previousLayers;
26
- private watcher;
27
- constructor(options: Options);
28
- start(): Promise<void>;
29
- readSetupJs(): Promise<string>;
30
- private onAll;
31
- private onReady;
32
- private build;
33
- private extractExportedClassNames;
34
- private generateLayerContent;
35
- private generateIndexContent;
36
- private generateSetupContent;
37
- private getLayer;
38
- private getLayerName;
39
- private capitalize;
40
- private decapitalize;
41
- private isTopLayer;
42
- private write;
43
- }
44
- declare function paralayer(options: Options): Promise<string>;
45
-
46
- export { type DirPath, type File, type Options, Paralayer, paralayer as default, paralayer };
package/dist/paralayer.js DELETED
@@ -1,194 +0,0 @@
1
- // src/paralayer.ts
2
- import { Queue, safe, Unit } from "@eposlabs/utils";
3
- import { watch } from "chokidar";
4
- import { mkdir, readFile, rm, writeFile } from "fs/promises";
5
- import { basename, extname, join, relative } from "path";
6
- var Paralayer = class extends Unit {
7
- files = {};
8
- options;
9
- started = false;
10
- ready = false;
11
- ready$ = Promise.withResolvers();
12
- queue = new Queue();
13
- extensions = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
14
- previousLayers = [];
15
- watcher = null;
16
- constructor(options) {
17
- super();
18
- this.options = options;
19
- }
20
- async start() {
21
- if (this.started) return;
22
- this.started = true;
23
- await safe(rm(this.options.output, { recursive: true }));
24
- this.watcher = watch(this.options.input);
25
- this.watcher.on("all", this.onAll);
26
- this.watcher.on("ready", this.onReady);
27
- await this.ready$.promise;
28
- await this.queue.run(() => this.build());
29
- if (!this.options.watch) await this.watcher.close();
30
- }
31
- async readSetupJs() {
32
- const setupJsPath = join(this.options.output, "setup.js");
33
- return await readFile(setupJsPath, "utf-8");
34
- }
35
- // ---------------------------------------------------------------------------
36
- // HANDLERS
37
- // ---------------------------------------------------------------------------
38
- onAll = async (event, path) => {
39
- if (!["add", "change", "unlink"].includes(event)) return;
40
- const ext = extname(path);
41
- if (!this.extensions.has(ext)) return;
42
- if (!this.getLayer(path)) return;
43
- if (!this.ready) {
44
- this.files[path] = null;
45
- return;
46
- }
47
- if (event === "unlink") {
48
- delete this.files[path];
49
- await this.queue.run(() => this.build());
50
- }
51
- if (event === "add" || event === "change") {
52
- this.files[path] = null;
53
- await this.queue.run(() => this.build());
54
- }
55
- };
56
- onReady = async () => {
57
- this.ready = true;
58
- this.ready$.resolve();
59
- };
60
- // ---------------------------------------------------------------------------
61
- // BUILD
62
- // ---------------------------------------------------------------------------
63
- async build() {
64
- const paths = Object.keys(this.files);
65
- if (paths.length === 0) return;
66
- const pathsByLayers = Object.groupBy(paths, (path) => this.getLayer(path));
67
- const allLayers = Object.keys(pathsByLayers);
68
- await mkdir(this.options.output, { recursive: true });
69
- await Promise.all(
70
- paths.map(async (path) => {
71
- if (this.files[path]) return;
72
- const content = await readFile(path, "utf-8");
73
- const names = this.extractExportedClassNames(content);
74
- this.files[path] = { content, names };
75
- })
76
- );
77
- for (const layer in pathsByLayers) {
78
- const layerFile = join(this.options.output, `layer.${layer}.ts`);
79
- const layerPaths = pathsByLayers[layer].toSorted();
80
- const layerContent = this.generateLayerContent(layer, layerPaths);
81
- await this.write(layerFile, layerContent);
82
- if (this.isTopLayer(layer)) {
83
- const indexFile = join(this.options.output, `index.${layer}.ts`);
84
- const indexContent = this.generateIndexContent(layer, allLayers);
85
- await this.write(indexFile, indexContent);
86
- }
87
- }
88
- const removedLayers = this.previousLayers.filter((l) => !allLayers.includes(l));
89
- this.previousLayers = allLayers;
90
- for (const layer of removedLayers) {
91
- const layerFile = join(this.options.output, `layer.${layer}.ts`);
92
- await rm(layerFile);
93
- if (this.isTopLayer(layer)) {
94
- const indexFile = join(this.options.output, `index.${layer}.ts`);
95
- await rm(indexFile);
96
- }
97
- }
98
- const setupFile = join(this.options.output, "setup.js");
99
- const setupContent = this.generateSetupContent(allLayers);
100
- await this.write(setupFile, setupContent);
101
- }
102
- // ---------------------------------------------------------------------------
103
- // HELPERS
104
- // ---------------------------------------------------------------------------
105
- extractExportedClassNames(content) {
106
- if (content.includes("paralayer-ignore")) return [];
107
- return content.split("export class ").slice(1).map((part) => part.split(" ")[0].split("<")[0]);
108
- }
109
- generateLayerContent(layer, layerPaths) {
110
- const $LayerName = this.getLayerName(layer, "$Pascal");
111
- const $layerName = this.getLayerName(layer, "$camel");
112
- const allNames = layerPaths.flatMap((path) => this.files[path]?.names ?? []);
113
- const imports = layerPaths.map((path) => {
114
- const file = this.files[path];
115
- if (!file) return "";
116
- if (file.names.length === 0) return "";
117
- const names = file.names;
118
- const types = file.names.map((name) => `type ${name} as ${name}Type`);
119
- const relativePath = relative(this.options.output, path);
120
- return `import { ${[...names, ...types].join(", ")} } from '${relativePath}'`;
121
- }).filter(Boolean);
122
- const assign = [`Object.assign(${$layerName}, {`, ...allNames.map((name) => ` ${name},`), `})`];
123
- const globals = [
124
- `declare global {`,
125
- ` var ${$layerName}: ${$LayerName}`,
126
- ``,
127
- ` interface ${$LayerName} {`,
128
- ...allNames.map((name) => ` ${name}: typeof ${name}`),
129
- ` }`,
130
- ``,
131
- ` namespace ${$layerName} {`,
132
- ...allNames.map((name) => ` export type ${name} = ${name}Type`),
133
- ` }`,
134
- `}`
135
- ];
136
- return [...imports, "", ...assign, "", ...globals, ""].join("\n");
137
- }
138
- generateIndexContent(topLayer, allLayers) {
139
- const imports = allLayers.filter((layer) => layer.includes(topLayer)).sort((layer1, layer2) => {
140
- if (layer1.length !== layer2.length) return layer2.length - layer1.length;
141
- return layer1.localeCompare(layer2);
142
- }).map((layer) => `import './layer.${layer}.ts'`);
143
- return [...imports].join("\n");
144
- }
145
- generateSetupContent(allLayers) {
146
- const layers = allLayers.toSorted((layer1, layer2) => {
147
- if (layer1.length !== layer2.length) return layer1.length - layer2.length;
148
- return layer1.localeCompare(layer2);
149
- });
150
- const vars = layers.map((layer) => {
151
- const $layerName = this.getLayerName(layer, "$camel");
152
- if (this.options.globalize) return `globalThis.${$layerName} = {}`;
153
- return `const ${$layerName} = {}`;
154
- });
155
- return [...vars].join("\n");
156
- }
157
- getLayer(path) {
158
- const name = basename(path);
159
- const layer = name.split(".").slice(1, -1).sort().join(".");
160
- if (layer) return layer;
161
- return this.options.defaultLayerName ?? "";
162
- }
163
- getLayerName(layer, style) {
164
- const LayerName = layer.split(".").map(this.capitalize).join("");
165
- if (style === "$camel") return `$${this.decapitalize(LayerName)}`;
166
- if (style === "$Pascal") return `$${LayerName}`;
167
- throw this.never;
168
- }
169
- capitalize(string) {
170
- return string.charAt(0).toUpperCase() + string.slice(1);
171
- }
172
- decapitalize(string) {
173
- return string.charAt(0).toLowerCase() + string.slice(1);
174
- }
175
- isTopLayer(layer) {
176
- return !layer.includes(".");
177
- }
178
- async write(path, content) {
179
- const [prevContent] = await safe(() => readFile(path, "utf-8"));
180
- if (content === prevContent) return;
181
- await writeFile(path, content, "utf-8");
182
- }
183
- };
184
- async function paralayer(options) {
185
- const pl = new Paralayer(options);
186
- await pl.start();
187
- return await pl.readSetupJs();
188
- }
189
- var paralayer_default = paralayer;
190
- export {
191
- Paralayer,
192
- paralayer_default as default,
193
- paralayer
194
- };