paralayer 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +30 -0
- package/dist/chunk-BES3GT56.js +195 -0
- package/dist/paralayer.d.ts +46 -0
- package/dist/paralayer.js +10 -0
- package/package.json +11 -7
- package/src/bin.ts +4 -4
- package/src/paralayer.ts +12 -4
- package/src/index.ts +0 -9
package/dist/bin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/bin.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
paralayer
|
|
4
|
+
} from "./chunk-BES3GT56.js";
|
|
5
|
+
|
|
6
|
+
// src/bin.ts
|
|
7
|
+
import minimist from "minimist";
|
|
8
|
+
var argv = minimist(process.argv.slice(2), {
|
|
9
|
+
string: ["defaultLayerName"],
|
|
10
|
+
boolean: ["watch", "globalize"],
|
|
11
|
+
default: { watch: false, globalize: false, defaultLayerName: null }
|
|
12
|
+
});
|
|
13
|
+
var paths = argv._;
|
|
14
|
+
var input = paths.slice(0, -1);
|
|
15
|
+
var output = paths.at(-1);
|
|
16
|
+
if (input.length === 0) {
|
|
17
|
+
console.error("[paralayer] Input directory is not provided");
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
if (!output) {
|
|
21
|
+
console.error("[paralayer] Output directory is not provided");
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
await paralayer({
|
|
25
|
+
input,
|
|
26
|
+
output,
|
|
27
|
+
watch: argv.watch,
|
|
28
|
+
globalize: argv.globalize,
|
|
29
|
+
defaultLayerName: argv.defaultLayerName
|
|
30
|
+
});
|
|
@@ -0,0 +1,195 @@
|
|
|
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
|
+
|
|
191
|
+
export {
|
|
192
|
+
Paralayer,
|
|
193
|
+
paralayer,
|
|
194
|
+
paralayer_default
|
|
195
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paralayer",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "imkost",
|
|
@@ -10,24 +10,28 @@
|
|
|
10
10
|
"vite-plugin"
|
|
11
11
|
],
|
|
12
12
|
"scripts": {
|
|
13
|
-
"
|
|
14
|
-
"build": "rm -rf dist && tsc",
|
|
13
|
+
"build": "tsup src --format esm --dts --clean",
|
|
15
14
|
"lint": "tsc --noEmit",
|
|
16
15
|
"release": "sh -c 'npm version ${1:-patch} && npm run build && npm publish' --"
|
|
17
16
|
},
|
|
18
17
|
"bin": {
|
|
19
|
-
"paralayer": "./
|
|
18
|
+
"paralayer": "./dist/bin.js",
|
|
19
|
+
"paralayer-ts": "./src/bin.ts"
|
|
20
20
|
},
|
|
21
21
|
"exports": {
|
|
22
22
|
".": {
|
|
23
|
-
"import": "./
|
|
23
|
+
"import": "./dist/paralayer.js"
|
|
24
|
+
},
|
|
25
|
+
"./ts": {
|
|
26
|
+
"import": "./src/paralayer.ts"
|
|
24
27
|
}
|
|
25
28
|
},
|
|
26
29
|
"files": [
|
|
27
|
-
"src"
|
|
30
|
+
"src",
|
|
31
|
+
"dist"
|
|
28
32
|
],
|
|
29
33
|
"dependencies": {
|
|
30
|
-
"@eposlabs/utils": "^1.
|
|
34
|
+
"@eposlabs/utils": "^1.1.1",
|
|
31
35
|
"chokidar": "^4.0.3",
|
|
32
36
|
"minimist": "^1.2.8"
|
|
33
37
|
},
|
package/src/bin.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import minimist from 'minimist'
|
|
4
|
-
import { paralayer } from './
|
|
4
|
+
import { paralayer } from './paralayer.ts'
|
|
5
5
|
|
|
6
6
|
const argv = minimist(process.argv.slice(2), {
|
|
7
|
-
string: ['
|
|
7
|
+
string: ['defaultLayerName'],
|
|
8
8
|
boolean: ['watch', 'globalize'],
|
|
9
|
-
default: { watch: false,
|
|
9
|
+
default: { watch: false, globalize: false, defaultLayerName: null },
|
|
10
10
|
})
|
|
11
11
|
|
|
12
12
|
const paths = argv._
|
|
@@ -27,6 +27,6 @@ await paralayer({
|
|
|
27
27
|
input: input,
|
|
28
28
|
output: output,
|
|
29
29
|
watch: argv.watch,
|
|
30
|
-
default: argv.default,
|
|
31
30
|
globalize: argv.globalize,
|
|
31
|
+
defaultLayerName: argv.defaultLayerName,
|
|
32
32
|
})
|
package/src/paralayer.ts
CHANGED
|
@@ -15,10 +15,10 @@ export type Options = {
|
|
|
15
15
|
input: DirPath | DirPath[]
|
|
16
16
|
output: DirPath
|
|
17
17
|
watch?: boolean
|
|
18
|
-
/** Default layer name. If a file name does not have layer tags, default name will be used. */
|
|
19
|
-
default?: string | null
|
|
20
18
|
/** Whether the layer variables should be exposed globally. */
|
|
21
19
|
globalize?: boolean
|
|
20
|
+
/** If a file name does not have layer tags, default layer name will be used. */
|
|
21
|
+
defaultLayerName?: string | null
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export class Paralayer extends Unit {
|
|
@@ -37,7 +37,7 @@ export class Paralayer extends Unit {
|
|
|
37
37
|
this.options = options
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
async start() {
|
|
41
41
|
if (this.started) return
|
|
42
42
|
this.started = true
|
|
43
43
|
|
|
@@ -238,7 +238,7 @@ export class Paralayer extends Unit {
|
|
|
238
238
|
const name = basename(path)
|
|
239
239
|
const layer = name.split('.').slice(1, -1).sort().join('.')
|
|
240
240
|
if (layer) return layer
|
|
241
|
-
return this.options.
|
|
241
|
+
return this.options.defaultLayerName ?? ''
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
private getLayerName(layer: string, style: '$camel' | '$Pascal') {
|
|
@@ -266,3 +266,11 @@ export class Paralayer extends Unit {
|
|
|
266
266
|
await writeFile(path, content, 'utf-8')
|
|
267
267
|
}
|
|
268
268
|
}
|
|
269
|
+
|
|
270
|
+
export async function paralayer(options: Options) {
|
|
271
|
+
const pl = new Paralayer(options)
|
|
272
|
+
await pl.start()
|
|
273
|
+
return await pl.readSetupJs()
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export default paralayer
|