@reliverse/dler 1.7.49 → 1.7.51
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 +8 -8
- package/bin/app/pack/cmd.d.ts +30 -6
- package/bin/app/pack/cmd.js +278 -95
- package/bin/libs/cfg/cfg-impl/rse-config/rse-impl/rse-define.d.ts +12 -12
- package/bin/libs/sdk/sdk-impl/config/info.js +1 -1
- package/bin/libs/sdk/sdk-impl/utils/resolve-cross-libs.js +0 -1
- package/bin/libs/sdk/sdk-mod.d.ts +0 -4
- package/bin/libs/sdk/sdk-mod.js +0 -15
- package/package.json +2 -1
- package/bin/app/unpack/cmd.d.ts +0 -37
- package/bin/app/unpack/cmd.js +0 -200
- package/bin/libs/sdk/sdk-impl/utils/pack-unpack/pu-constants.d.ts +0 -6
- package/bin/libs/sdk/sdk-impl/utils/pack-unpack/pu-constants.js +0 -7
- package/bin/libs/sdk/sdk-impl/utils/pack-unpack/pu-file-utils.d.ts +0 -5
- package/bin/libs/sdk/sdk-impl/utils/pack-unpack/pu-file-utils.js +0 -40
- package/bin/libs/sdk/sdk-impl/utils/pack-unpack/pu-types.d.ts +0 -24
- package/bin/libs/sdk/sdk-impl/utils/pack-unpack/pu-types.js +0 -0
- package/bin/libs/sdk/sdk-impl/utils/pack-unpack/pub-json-utils.d.ts +0 -17
- package/bin/libs/sdk/sdk-impl/utils/pack-unpack/pub-json-utils.js +0 -46
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
### ⚡ developer experience
|
|
18
18
|
|
|
19
19
|
- **performance optimized** for speed with modern build pipelines and caching
|
|
20
|
-
- **
|
|
20
|
+
- **16 built-in commands** — comprehensive [dler commands](#dler-commands) for every workflow
|
|
21
21
|
- **path resolution magic** converts typescript aliases to relative imports automatically
|
|
22
22
|
- **highly configurable** via dedicated configuration files with sensible defaults
|
|
23
23
|
- **dual interface** — cli for everyday use, sdk for advanced programmatic control
|
|
@@ -148,7 +148,7 @@ bun dev # bun src/cli.ts --dev
|
|
|
148
148
|
|
|
149
149
|
## dler commands
|
|
150
150
|
|
|
151
|
-
dler ships with a flexible command system (prev. plugins) and **
|
|
151
|
+
dler ships with a flexible command system (prev. plugins) and **16 built-in commands** (from [reliverse addons](https://reliverse.org/addons) collection).
|
|
152
152
|
|
|
153
153
|
feel free to create your own commands. commands can be implemented as built-in directly in `src/app/<command>/impl/*` and then imported from `src/app/<command>/cmd.ts`; or implemented in your own library and then imported from `src/app/<command>/cmd.ts`.
|
|
154
154
|
|
|
@@ -156,7 +156,7 @@ if you run just `dler` — it will display a list of commands which you can laun
|
|
|
156
156
|
|
|
157
157
|
## **available commands**
|
|
158
158
|
|
|
159
|
-
[build](#1-build) — [pub](#2-pub) — [agg](#3-agg) — [check](#4-check) — [conv](#5-conv) — [copy](#6-copy) — [init](#7-init) — [inject](#8-inject) — [libs](#9-libs) — [merge](#10-merge) — [migrate](#11-migrate) — [rempts](#12-rempts) — [rename](#13-rename) — [spell](#14-magic) — [split](#15-split) — [pack](#16-pack)
|
|
159
|
+
[build](#1-build) — [pub](#2-pub) — [agg](#3-agg) — [check](#4-check) — [conv](#5-conv) — [copy](#6-copy) — [init](#7-init) — [inject](#8-inject) — [libs](#9-libs) — [merge](#10-merge) — [migrate](#11-migrate) — [rempts](#12-rempts) — [rename](#13-rename) — [spell](#14-magic) — [split](#15-split) — [pack](#16-pack)
|
|
160
160
|
|
|
161
161
|
### 1. `build`
|
|
162
162
|
|
|
@@ -798,7 +798,7 @@ output/
|
|
|
798
798
|
└── mod.ts
|
|
799
799
|
```
|
|
800
800
|
|
|
801
|
-
|
|
801
|
+
**--unpack**:
|
|
802
802
|
|
|
803
803
|
creates file structure from packed templates. This command is the counterpart to `pack` and is used to extract and restore template files from a packed template package.
|
|
804
804
|
|
|
@@ -818,16 +818,16 @@ creates file structure from packed templates. This command is the counterpart to
|
|
|
818
818
|
|
|
819
819
|
```bash
|
|
820
820
|
# Basic usage
|
|
821
|
-
dler
|
|
821
|
+
dler pack ./dist-templates --output ./my-project --unpack
|
|
822
822
|
|
|
823
823
|
# With custom output directory
|
|
824
|
-
dler
|
|
824
|
+
dler pack ./dist-templates --output ./custom-location --unpack
|
|
825
825
|
|
|
826
826
|
# Preview changes without applying
|
|
827
|
-
dler
|
|
827
|
+
dler pack ./dist-templates --output ./my-project --dry-run --unpack
|
|
828
828
|
|
|
829
829
|
# Clean up existing template files before unpacking
|
|
830
|
-
dler
|
|
830
|
+
dler pack ./dist-templates --output ./my-project --cleanup --unpack
|
|
831
831
|
```
|
|
832
832
|
|
|
833
833
|
**arguments:**
|
package/bin/app/pack/cmd.d.ts
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
|
+
interface FileMetadata {
|
|
2
|
+
updatedAt?: string;
|
|
3
|
+
updatedHash?: string;
|
|
4
|
+
}
|
|
5
|
+
interface TemplatesFileContent {
|
|
6
|
+
content: FileContent;
|
|
7
|
+
type: "text" | "json" | "binary";
|
|
8
|
+
hasError?: boolean;
|
|
9
|
+
error?: string;
|
|
10
|
+
jsonComments?: Record<number, string>;
|
|
11
|
+
binaryHash?: string;
|
|
12
|
+
metadata?: FileMetadata;
|
|
13
|
+
}
|
|
14
|
+
type FileContent = string | Record<string, unknown>;
|
|
15
|
+
/** Escape back-`s, ${ and newlines for safe template literal embedding */
|
|
16
|
+
export declare const escapeTemplateString: (str: string) => string;
|
|
17
|
+
export declare const unescapeTemplateString: (str: string) => string;
|
|
18
|
+
export declare const hashFile: (file: string) => Promise<string>;
|
|
19
|
+
export declare const getFileMetadata: (file: string) => Promise<FileMetadata>;
|
|
20
|
+
/** Recursively walk a directory */
|
|
21
|
+
export declare const walkDir: (dir: string) => Promise<string[]>;
|
|
22
|
+
/** Process a file and return the TemplatesFileContent structure */
|
|
23
|
+
export declare const readFileForTemplate: (absPath: string, relPath: string, binariesOutDir: string) => Promise<TemplatesFileContent>;
|
|
1
24
|
declare const _default: import("@reliverse/rempts").Command<{
|
|
2
|
-
|
|
25
|
+
input: {
|
|
3
26
|
type: "positional";
|
|
4
27
|
required: true;
|
|
5
28
|
description: string;
|
|
@@ -11,7 +34,7 @@ declare const _default: import("@reliverse/rempts").Command<{
|
|
|
11
34
|
};
|
|
12
35
|
whitelabel: {
|
|
13
36
|
type: "string";
|
|
14
|
-
default:
|
|
37
|
+
default: string;
|
|
15
38
|
description: string;
|
|
16
39
|
};
|
|
17
40
|
cdn: {
|
|
@@ -28,10 +51,6 @@ declare const _default: import("@reliverse/rempts").Command<{
|
|
|
28
51
|
default: true;
|
|
29
52
|
description: string;
|
|
30
53
|
};
|
|
31
|
-
/**
|
|
32
|
-
* - Without --files: All files are checked and updated if they're newer or have different content
|
|
33
|
-
* - With --files: Only specified files are checked and updated if they're newer or have different content
|
|
34
|
-
*/
|
|
35
54
|
files: {
|
|
36
55
|
type: "string";
|
|
37
56
|
description: string;
|
|
@@ -40,5 +59,10 @@ declare const _default: import("@reliverse/rempts").Command<{
|
|
|
40
59
|
type: "string";
|
|
41
60
|
description: string;
|
|
42
61
|
};
|
|
62
|
+
unpack: {
|
|
63
|
+
type: "boolean";
|
|
64
|
+
default: false;
|
|
65
|
+
description: string;
|
|
66
|
+
};
|
|
43
67
|
}>;
|
|
44
68
|
export default _default;
|
package/bin/app/pack/cmd.js
CHANGED
|
@@ -1,41 +1,216 @@
|
|
|
1
1
|
import path from "@reliverse/pathkit";
|
|
2
2
|
import { relinka } from "@reliverse/relinka";
|
|
3
|
-
import {
|
|
3
|
+
import { defineCommand, defineArgs } from "@reliverse/rempts";
|
|
4
4
|
import { createJiti } from "jiti";
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const jiti = createJiti(import.meta.url);
|
|
19
|
-
const hashFile = async (file) => {
|
|
20
|
-
const buff = await fs.readFile(file);
|
|
21
|
-
return createHash("sha1").update(buff).digest("hex").slice(0, 10);
|
|
6
|
+
import fs from "node:fs/promises";
|
|
7
|
+
import stripJsonComments from "strip-json-comments";
|
|
8
|
+
import { isBinaryExt } from "../../libs/sdk/sdk-impl/utils/binary.js";
|
|
9
|
+
const WHITELABEL_DEFAULT = "DLER";
|
|
10
|
+
const TEMPLATE_VAR = (name, whitelabel) => `${whitelabel}_TPL_${name.toUpperCase()}`;
|
|
11
|
+
const TPLS_DIR = "templates";
|
|
12
|
+
const BINARIES_DIR = "binaries";
|
|
13
|
+
export const escapeTemplateString = (str) => str.replace(/`/g, "\\`").replace(/\\\${/g, "\\u0024{").replace(/\$\{/g, "\\${").replace(/\\\{\{/g, "{{").replace(/\\\}\}/g, "}}").replace(/\r?\n/g, "\\n");
|
|
14
|
+
export const unescapeTemplateString = (str) => str.replace(/\\n/g, "\n").replace(/\\u0024\{/g, "\\${").replace(/\\\\`/g, "`").replace(/\\\\/g, "\\");
|
|
15
|
+
export const hashFile = async (file) => {
|
|
16
|
+
const buf = await fs.readFile(file);
|
|
17
|
+
return createHash("sha1").update(buf).digest("hex").slice(0, 10);
|
|
22
18
|
};
|
|
23
|
-
const getFileMetadata = async (file) => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
export const getFileMetadata = async (file) => {
|
|
20
|
+
try {
|
|
21
|
+
const [stats, hash] = await Promise.all([fs.stat(file), hashFile(file)]);
|
|
22
|
+
return {
|
|
23
|
+
updatedAt: stats.mtime.toISOString(),
|
|
24
|
+
updatedHash: hash
|
|
25
|
+
};
|
|
26
|
+
} catch (err) {
|
|
27
|
+
relinka("warn", `Failed to get metadata for ${file}: ${err.message}`);
|
|
28
|
+
return {
|
|
29
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
30
|
+
updatedHash: ""
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
export const walkDir = async (dir) => {
|
|
35
|
+
let res = [];
|
|
36
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
const full = path.join(dir, entry.name);
|
|
39
|
+
if (entry.isDirectory()) {
|
|
40
|
+
res = res.concat(await walkDir(full));
|
|
41
|
+
} else {
|
|
42
|
+
res.push(full);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return res;
|
|
46
|
+
};
|
|
47
|
+
export const readFileForTemplate = async (absPath, relPath, binariesOutDir) => {
|
|
48
|
+
const metadata = await getFileMetadata(absPath);
|
|
49
|
+
try {
|
|
50
|
+
if (await isBinaryExt(absPath)) {
|
|
51
|
+
const hash = metadata.updatedHash;
|
|
52
|
+
const ext2 = path.extname(absPath);
|
|
53
|
+
const target = path.join(binariesOutDir, `${hash}${ext2}`);
|
|
54
|
+
try {
|
|
55
|
+
await fs.mkdir(binariesOutDir, { recursive: true });
|
|
56
|
+
await fs.copyFile(absPath, target).catch(async (err) => {
|
|
57
|
+
if (err.code !== "EEXIST") throw err;
|
|
58
|
+
});
|
|
59
|
+
} catch (err) {
|
|
60
|
+
relinka("error", `Failed copying binary ${relPath}: ${err.message}`);
|
|
61
|
+
return {
|
|
62
|
+
content: "",
|
|
63
|
+
type: "binary",
|
|
64
|
+
hasError: true,
|
|
65
|
+
error: err.message,
|
|
66
|
+
binaryHash: hash,
|
|
67
|
+
metadata
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
content: "",
|
|
72
|
+
type: "binary",
|
|
73
|
+
binaryHash: hash,
|
|
74
|
+
metadata
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const raw = await fs.readFile(absPath, "utf8");
|
|
78
|
+
const ext = path.extname(absPath).toLowerCase();
|
|
79
|
+
if (ext === ".json") {
|
|
80
|
+
const comments = {};
|
|
81
|
+
const lines = raw.split(/\r?\n/);
|
|
82
|
+
lines.forEach((line, idx) => {
|
|
83
|
+
const trimmed = line.trim();
|
|
84
|
+
if (trimmed.startsWith("//") || trimmed.startsWith("/*")) comments[idx + 1] = line;
|
|
85
|
+
});
|
|
86
|
+
try {
|
|
87
|
+
const parsed = JSON.parse(stripJsonComments(raw));
|
|
88
|
+
return {
|
|
89
|
+
content: parsed,
|
|
90
|
+
type: "json",
|
|
91
|
+
jsonComments: Object.keys(comments).length ? comments : void 0,
|
|
92
|
+
metadata
|
|
93
|
+
};
|
|
94
|
+
} catch (err) {
|
|
95
|
+
relinka("warn", `Failed to parse JSON file ${relPath}: ${err.message}`);
|
|
96
|
+
return {
|
|
97
|
+
content: {},
|
|
98
|
+
type: "json",
|
|
99
|
+
hasError: true,
|
|
100
|
+
error: err.message,
|
|
101
|
+
jsonComments: Object.keys(comments).length ? comments : void 0,
|
|
102
|
+
metadata
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
content: raw,
|
|
108
|
+
type: "text",
|
|
109
|
+
metadata
|
|
110
|
+
};
|
|
111
|
+
} catch (err) {
|
|
112
|
+
relinka("warn", `Failed to read file ${relPath}: ${err.message}`);
|
|
113
|
+
return {
|
|
114
|
+
content: "",
|
|
115
|
+
type: "text",
|
|
116
|
+
hasError: true,
|
|
117
|
+
error: err.message,
|
|
118
|
+
metadata
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
const findTemplatesObject = (mod) => {
|
|
123
|
+
if (mod.DLER_TEMPLATES && typeof mod.DLER_TEMPLATES === "object") {
|
|
124
|
+
return mod.DLER_TEMPLATES;
|
|
125
|
+
}
|
|
126
|
+
if (mod.default && typeof mod.default === "object") {
|
|
127
|
+
return mod.default;
|
|
128
|
+
}
|
|
129
|
+
for (const v of Object.values(mod)) {
|
|
130
|
+
if (v && typeof v === "object" && Object.values(v).every((t) => t && typeof t === "object" && "config" in t)) {
|
|
131
|
+
return v;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return {};
|
|
135
|
+
};
|
|
136
|
+
const restoreFile = async (outRoot, relPath, meta, binsDir, force = false) => {
|
|
137
|
+
const dest = path.join(outRoot, relPath);
|
|
138
|
+
await fs.mkdir(path.dirname(dest), { recursive: true });
|
|
139
|
+
try {
|
|
140
|
+
if (!force) {
|
|
141
|
+
await fs.access(dest);
|
|
142
|
+
relinka("log", `Skipping existing file (use --force to overwrite): ${relPath}`);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
} catch {
|
|
146
|
+
}
|
|
147
|
+
if (meta.type === "binary") {
|
|
148
|
+
const ext = path.extname(relPath);
|
|
149
|
+
const src = path.join(binsDir, `${meta.binaryHash}${ext}`);
|
|
150
|
+
try {
|
|
151
|
+
await fs.copyFile(src, dest);
|
|
152
|
+
} catch (err) {
|
|
153
|
+
relinka("error", `Missing binary ${src} for ${relPath}: ${err.message}`);
|
|
154
|
+
}
|
|
155
|
+
} else if (meta.type === "json") {
|
|
156
|
+
const jsonStr = JSON.stringify(meta.content, null, 2);
|
|
157
|
+
await fs.writeFile(dest, jsonStr, "utf8");
|
|
158
|
+
} else {
|
|
159
|
+
await fs.writeFile(dest, meta.content, "utf8");
|
|
160
|
+
}
|
|
161
|
+
if (meta.metadata?.updatedAt) {
|
|
162
|
+
const ts = new Date(meta.metadata.updatedAt);
|
|
163
|
+
await fs.utimes(dest, ts, ts);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
const unpackTemplates = async (aggregatorPath, outDir, force = false) => {
|
|
167
|
+
const jiti = createJiti(process.cwd());
|
|
168
|
+
let mod;
|
|
169
|
+
try {
|
|
170
|
+
mod = await jiti.import(aggregatorPath);
|
|
171
|
+
} catch (err) {
|
|
172
|
+
throw new Error(`Failed to import aggregator file: ${err.message}`);
|
|
173
|
+
}
|
|
174
|
+
const templatesObj = findTemplatesObject(mod);
|
|
175
|
+
if (!Object.keys(templatesObj).length) {
|
|
176
|
+
throw new Error("No templates found in aggregator file.");
|
|
177
|
+
}
|
|
178
|
+
const binsDir = path.join(path.dirname(aggregatorPath), TPLS_DIR, BINARIES_DIR);
|
|
179
|
+
let unpackedFiles = 0;
|
|
180
|
+
for (const [tplName, tpl] of Object.entries(templatesObj)) {
|
|
181
|
+
relinka("info", `Unpacking template: ${tplName}`);
|
|
182
|
+
for (const [relPath, meta] of Object.entries(tpl.config.files)) {
|
|
183
|
+
try {
|
|
184
|
+
await restoreFile(outDir, relPath, meta, binsDir, force);
|
|
185
|
+
unpackedFiles++;
|
|
186
|
+
} catch (err) {
|
|
187
|
+
relinka("error", `Failed restoring ${relPath}: ${err.message}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
relinka("success", `Unpacked ${unpackedFiles} files into ${outDir}`);
|
|
192
|
+
};
|
|
193
|
+
const writeTypesFile = async (outRoot, outputName) => {
|
|
194
|
+
const typesFile = path.join(outRoot, `${outputName}-types.ts`);
|
|
195
|
+
const code = `// Auto-generated type declarations for templates.
|
|
196
|
+
export interface FileMetadata { updatedAt?: string; updatedHash?: string; }
|
|
197
|
+
export interface TemplatesFileContent { content: string | Record<string, unknown>; type: 'text' | 'json' | 'binary'; hasError?: boolean; error?: string; jsonComments?: Record<number, string>; binaryHash?: string; metadata?: FileMetadata; }
|
|
198
|
+
export interface Template { name: string; description: string; config: { files: Record<string, TemplatesFileContent> }; updatedAt?: string; }
|
|
199
|
+
`;
|
|
200
|
+
await fs.writeFile(typesFile, code, "utf8");
|
|
30
201
|
};
|
|
31
202
|
export default defineCommand({
|
|
32
203
|
meta: {
|
|
33
204
|
name: "pack",
|
|
34
|
-
version: "1.
|
|
35
|
-
description: "
|
|
205
|
+
version: "1.2.0",
|
|
206
|
+
description: "Pack templates into TS modules or --unpack them back to files"
|
|
36
207
|
},
|
|
37
208
|
args: defineArgs({
|
|
38
|
-
|
|
209
|
+
input: {
|
|
210
|
+
type: "positional",
|
|
211
|
+
required: true,
|
|
212
|
+
description: "Input directory (pack) or aggregator file (unpack)"
|
|
213
|
+
},
|
|
39
214
|
output: { type: "string", default: "my-templates", description: "Output dir" },
|
|
40
215
|
whitelabel: { type: "string", default: WHITELABEL_DEFAULT, description: "Rename DLER" },
|
|
41
216
|
cdn: {
|
|
@@ -46,28 +221,45 @@ export default defineCommand({
|
|
|
46
221
|
update: {
|
|
47
222
|
type: "boolean",
|
|
48
223
|
default: true,
|
|
49
|
-
description: "Update existing templates and add new ones
|
|
50
|
-
},
|
|
51
|
-
/**
|
|
52
|
-
* - Without --files: All files are checked and updated if they're newer or have different content
|
|
53
|
-
* - With --files: Only specified files are checked and updated if they're newer or have different content
|
|
54
|
-
*/
|
|
55
|
-
files: {
|
|
56
|
-
type: "string",
|
|
57
|
-
description: "Comma-separated list of specific files to update (relative to template dir)"
|
|
224
|
+
description: "Update existing templates and add new ones (pack mode only)"
|
|
58
225
|
},
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
226
|
+
files: { type: "string", description: "Comma-separated list of specific files to update" },
|
|
227
|
+
lastUpdate: { type: "string", description: "Override lastUpdate timestamp (pack mode only)" },
|
|
228
|
+
unpack: {
|
|
229
|
+
type: "boolean",
|
|
230
|
+
default: false,
|
|
231
|
+
description: "Unpack templates from an aggregator file"
|
|
62
232
|
}
|
|
63
233
|
}),
|
|
64
234
|
async run({ args }) {
|
|
65
235
|
if (args.cdn) throw new Error("Remote CDN support is not implemented yet.");
|
|
66
|
-
|
|
236
|
+
if (args.unpack) {
|
|
237
|
+
const aggrPath = path.resolve(args.input);
|
|
238
|
+
const outDir2 = path.resolve(args.output);
|
|
239
|
+
try {
|
|
240
|
+
const stat = await fs.stat(aggrPath);
|
|
241
|
+
if (!stat.isFile() || !aggrPath.endsWith(".ts")) {
|
|
242
|
+
throw new Error("Input for --unpack must be a TypeScript aggregator file (*.ts)");
|
|
243
|
+
}
|
|
244
|
+
} catch (err) {
|
|
245
|
+
relinka("error", err.message);
|
|
246
|
+
process.exit(1);
|
|
247
|
+
}
|
|
248
|
+
relinka("info", `Unpacking templates from ${aggrPath} to ${outDir2}`);
|
|
249
|
+
try {
|
|
250
|
+
await unpackTemplates(aggrPath, outDir2, args.force);
|
|
251
|
+
} catch (err) {
|
|
252
|
+
relinka("error", err.message);
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const dirToProcess = path.resolve(args.input);
|
|
67
258
|
const outDir = path.resolve(args.output);
|
|
68
259
|
const outDirName = path.basename(outDir);
|
|
69
260
|
const typesFile = `${outDirName}-types.ts`;
|
|
70
261
|
const modFile = `${outDirName}-mod.ts`;
|
|
262
|
+
relinka("info", `Packing templates from ${dirToProcess} to ${outDir}`);
|
|
71
263
|
const filesToUpdate = args.files ? new Set(args.files.split(",").map((f) => f.trim())) : null;
|
|
72
264
|
let existingTemplates = {};
|
|
73
265
|
try {
|
|
@@ -84,6 +276,7 @@ export default defineCommand({
|
|
|
84
276
|
if (args.update) {
|
|
85
277
|
try {
|
|
86
278
|
const modPath = path.join(outDir, modFile);
|
|
279
|
+
const jiti = createJiti(process.cwd());
|
|
87
280
|
const mod2 = await jiti.import(modPath);
|
|
88
281
|
existingTemplates = mod2?.DLER_TEMPLATES || mod2?.default || {};
|
|
89
282
|
} catch (loadError) {
|
|
@@ -105,36 +298,13 @@ export default defineCommand({
|
|
|
105
298
|
try {
|
|
106
299
|
await fs.access(path.join(outDir, typesFile));
|
|
107
300
|
} catch {
|
|
108
|
-
|
|
109
|
-
export interface FileMetadata {
|
|
110
|
-
updatedAt?: string;
|
|
111
|
-
updatedHash?: string;
|
|
112
|
-
}
|
|
113
|
-
export interface TemplatesFileContent {
|
|
114
|
-
content: FileContent;
|
|
115
|
-
type: "text" | "json" | "binary";
|
|
116
|
-
hasError?: boolean;
|
|
117
|
-
jsonComments?: Record<number, string>;
|
|
118
|
-
binaryHash?: string;
|
|
119
|
-
metadata?: FileMetadata;
|
|
120
|
-
}
|
|
121
|
-
export interface TemplateConfig {
|
|
122
|
-
files: Record<string, TemplatesFileContent>;
|
|
123
|
-
}
|
|
124
|
-
export interface Template {
|
|
125
|
-
name: string;
|
|
126
|
-
description: string;
|
|
127
|
-
config: TemplateConfig;
|
|
128
|
-
updatedAt?: string;
|
|
129
|
-
}
|
|
130
|
-
export interface Templates extends Record<string, Template> {}
|
|
131
|
-
`;
|
|
132
|
-
await fs.writeFile(path.join(outDir, typesFile), typesContent);
|
|
301
|
+
await writeTypesFile(outDir, outDirName);
|
|
133
302
|
}
|
|
134
303
|
const aggregatedImports = [];
|
|
135
304
|
const aggregatedEntries = [];
|
|
136
305
|
const mapEntries = [];
|
|
137
306
|
for (const tplName of templateDirs) {
|
|
307
|
+
relinka("info", `Processing template: ${tplName}`);
|
|
138
308
|
const absTplDir = path.join(dirToProcess, tplName);
|
|
139
309
|
const allFiles = await walkDir(absTplDir);
|
|
140
310
|
const filesRecord = {};
|
|
@@ -151,11 +321,15 @@ export interface Templates extends Record<string, Template> {}
|
|
|
151
321
|
const fileMetadata = await getFileMetadata(absFile);
|
|
152
322
|
const existingFile = existingFiles[rel];
|
|
153
323
|
const existingMetadata = existingFile?.metadata;
|
|
154
|
-
if (existingMetadata && (existingMetadata.updatedHash === fileMetadata.updatedHash || fileMetadata.updatedAt <= existingMetadata.updatedAt)) {
|
|
324
|
+
if (existingMetadata && existingMetadata.updatedHash && fileMetadata.updatedHash && (existingMetadata.updatedHash === fileMetadata.updatedHash || fileMetadata.updatedAt && existingMetadata.updatedAt && fileMetadata.updatedAt <= existingMetadata.updatedAt)) {
|
|
155
325
|
filesRecord[rel] = existingFile;
|
|
156
326
|
continue;
|
|
157
327
|
}
|
|
158
|
-
const meta = await readFileForTemplate(
|
|
328
|
+
const meta = await readFileForTemplate(
|
|
329
|
+
absFile,
|
|
330
|
+
rel,
|
|
331
|
+
path.join(outDir, TPLS_DIR, BINARIES_DIR)
|
|
332
|
+
);
|
|
159
333
|
if (meta.type === "binary") {
|
|
160
334
|
const hash = await hashFile(absFile);
|
|
161
335
|
const ext = path.extname(absFile);
|
|
@@ -176,12 +350,6 @@ export interface Templates extends Record<string, Template> {}
|
|
|
176
350
|
continue;
|
|
177
351
|
}
|
|
178
352
|
if (meta.type === "json") {
|
|
179
|
-
if (rel.endsWith("package.json")) {
|
|
180
|
-
meta.content.__satisfies = "PackageJson";
|
|
181
|
-
}
|
|
182
|
-
if (rel.endsWith("tsconfig.json")) {
|
|
183
|
-
meta.content.__satisfies = "TSConfig";
|
|
184
|
-
}
|
|
185
353
|
}
|
|
186
354
|
filesRecord[rel] = {
|
|
187
355
|
...meta,
|
|
@@ -230,31 +398,33 @@ export interface Templates extends Record<string, Template> {}
|
|
|
230
398
|
code.push(` content: \`${escapeTemplateString(meta.content)}\`,`);
|
|
231
399
|
code.push(' type: "text",');
|
|
232
400
|
} else {
|
|
233
|
-
|
|
401
|
+
let clone;
|
|
234
402
|
let sat = "";
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
403
|
+
try {
|
|
404
|
+
clone = JSON.parse(JSON.stringify(meta.content));
|
|
405
|
+
if (rel.endsWith("package.json")) {
|
|
406
|
+
sat = " satisfies PackageJson";
|
|
407
|
+
} else if (rel.endsWith("tsconfig.json")) {
|
|
408
|
+
sat = " satisfies TSConfig";
|
|
409
|
+
}
|
|
410
|
+
} catch (error) {
|
|
411
|
+
clone = {};
|
|
412
|
+
relinka("error", `Failed to process JSON content for ${rel}: ${error}`);
|
|
239
413
|
}
|
|
240
|
-
const jsonStr = JSON.stringify(
|
|
241
|
-
clone,
|
|
242
|
-
(key, value) => {
|
|
243
|
-
if (typeof key === "string" && /^[a-zA-Z0-9_]+$/.test(key)) {
|
|
244
|
-
return value;
|
|
245
|
-
}
|
|
246
|
-
return value;
|
|
247
|
-
},
|
|
248
|
-
2
|
|
249
|
-
).split("\n").map((line, i) => {
|
|
414
|
+
const jsonStr = JSON.stringify(clone, null, 2).split("\n").map((line, i) => {
|
|
250
415
|
if (i === 0) return line;
|
|
251
416
|
return " " + line.replace(/"([a-zA-Z0-9_]+)":/g, "$1:");
|
|
252
|
-
}).join("\n").replace(
|
|
417
|
+
}).join("\n").replace(/,?\s*}(\s*)$/, "}$1").replace(/,\s*,/g, ",");
|
|
253
418
|
code.push(` content: ${jsonStr}${sat},`);
|
|
254
419
|
code.push(' type: "json",');
|
|
255
420
|
}
|
|
256
|
-
if (meta.hasError)
|
|
257
|
-
|
|
421
|
+
if (meta.hasError) {
|
|
422
|
+
code.push(" hasError: true,");
|
|
423
|
+
if (meta.error) {
|
|
424
|
+
code.push(` error: "${escapeTemplateString(meta.error)}",`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
code.push(` }${isLast ? "" : ","}`);
|
|
258
428
|
});
|
|
259
429
|
code.push(" },");
|
|
260
430
|
code.push(" },");
|
|
@@ -281,6 +451,13 @@ export interface Templates extends Record<string, Template> {}
|
|
|
281
451
|
} else if (!args.update) {
|
|
282
452
|
relinka("log", `Creating template: ${tplName}`);
|
|
283
453
|
}
|
|
454
|
+
const filesWithErrors = Object.entries(filesRecord).filter(([_, meta]) => meta.hasError);
|
|
455
|
+
if (filesWithErrors.length > 0) {
|
|
456
|
+
relinka("warn", `Template ${tplName} has files with errors:`);
|
|
457
|
+
for (const [file, meta] of filesWithErrors) {
|
|
458
|
+
relinka("warn", ` - ${file}: ${meta.error || "Unknown error"}`);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
284
461
|
await fs.writeFile(templatePath, code.join("\n"));
|
|
285
462
|
aggregatedImports.push(`import { ${varName} } from "./${TPLS_DIR}/${tplName}";`);
|
|
286
463
|
aggregatedEntries.push(` ${tplName}: ${varName},`);
|
|
@@ -294,11 +471,11 @@ export interface Templates extends Record<string, Template> {}
|
|
|
294
471
|
...aggregatedEntries,
|
|
295
472
|
"};",
|
|
296
473
|
"",
|
|
297
|
-
`export const ${WL}_TEMPLATES = ${WL}_TEMPLATES_OBJ
|
|
474
|
+
`export const ${WL}_TEMPLATES = ${WL}_TEMPLATES_OBJ;`,
|
|
298
475
|
"",
|
|
299
|
-
`export
|
|
476
|
+
`export type ${WL}_TEMPLATE_NAMES = keyof typeof ${WL}_TEMPLATES;`,
|
|
300
477
|
"",
|
|
301
|
-
`export const
|
|
478
|
+
`export const ${WL.toLowerCase()}TemplatesMap: Record<string, ${WL}_TEMPLATE_NAMES> = {`,
|
|
302
479
|
...mapEntries,
|
|
303
480
|
"};"
|
|
304
481
|
];
|
|
@@ -306,9 +483,15 @@ export interface Templates extends Record<string, Template> {}
|
|
|
306
483
|
const templatePaths = templateDirs.map(
|
|
307
484
|
(tpl) => path.relative(process.cwd(), path.join(outDir, TPLS_DIR, `${tpl}.ts`))
|
|
308
485
|
);
|
|
309
|
-
relinka("
|
|
486
|
+
relinka("success", `Packed ${templateDirs.length} templates into ${modFile}:`);
|
|
310
487
|
for (const p of templatePaths) {
|
|
311
488
|
relinka("log", `- ${p}`);
|
|
312
489
|
}
|
|
490
|
+
const binaryCount = Object.values(existingTemplates).reduce((count, template) => {
|
|
491
|
+
return count + Object.values(template.config.files).filter((file) => file.type === "binary").length;
|
|
492
|
+
}, 0);
|
|
493
|
+
if (binaryCount > 0) {
|
|
494
|
+
relinka("info", ` - ${TPLS_DIR}/${BINARIES_DIR}/* (${binaryCount} binary files)`);
|
|
495
|
+
}
|
|
313
496
|
}
|
|
314
497
|
});
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import type { RseConfig } from "./rse-types";
|
|
2
2
|
export declare const defineConfigRse: (userConfig?: Partial<RseConfig>) => {
|
|
3
|
-
version?: string | undefined;
|
|
4
3
|
$schema?: "./schema.json" | "https://reliverse.org/schema.json" | undefined;
|
|
5
4
|
projectName?: string | undefined;
|
|
6
5
|
projectAuthor?: string | undefined;
|
|
7
6
|
projectDescription?: string | undefined;
|
|
7
|
+
version?: string | undefined;
|
|
8
8
|
projectLicense?: string | undefined;
|
|
9
9
|
projectRepository?: string | undefined;
|
|
10
10
|
projectDomain?: string | undefined;
|
|
11
11
|
projectGitService?: "none" | "github" | "gitlab" | "bitbucket" | undefined;
|
|
12
12
|
projectDeployService?: "none" | "vercel" | "netlify" | "railway" | "deno" | undefined;
|
|
13
|
-
projectPackageManager?: "npm" | "
|
|
14
|
-
projectState?: "
|
|
15
|
-
projectCategory?: "
|
|
13
|
+
projectPackageManager?: "npm" | "pnpm" | "bun" | "yarn" | undefined;
|
|
14
|
+
projectState?: "creating" | "created" | undefined;
|
|
15
|
+
projectCategory?: "cli" | "unknown" | "website" | "vscode" | "browser" | "library" | "mobile" | undefined;
|
|
16
16
|
projectSubcategory?: "unknown" | "e-commerce" | "tool" | undefined;
|
|
17
|
-
projectFramework?: "rempts" | "npm-jsr" | "
|
|
17
|
+
projectFramework?: "rempts" | "npm-jsr" | "unknown" | "vscode" | "nextjs" | "vite" | "svelte" | "remix" | "astro" | "nuxt" | "solid" | "qwik" | "vue" | "wxt" | "lynx" | "react-native" | "expo" | "capacitor" | "ionic" | "electron" | "tauri" | "neutralino" | "citty" | "commander" | "cac" | "meow" | "yargs" | "webextension" | "browser-extension" | undefined;
|
|
18
18
|
projectTemplate?: "unknown" | "blefnk/relivator-nextjs-template" | "blefnk/relivator-docker-template" | "blefnk/next-react-ts-src-minimal" | "blefnk/all-in-one-nextjs-template" | "blefnk/create-t3-app" | "blefnk/create-next-app" | "blefnk/astro-starlight-template" | "blefnk/versator-nextjs-template" | "blefnk/relivator-lynxjs-template" | "blefnk/relivator-react-native-template" | "reliverse/template-browser-extension" | "microsoft/vscode-extension-samples" | "microsoft/vscode-extension-template" | "rsetarter-template" | "blefnk/deno-cli-tutorial" | undefined;
|
|
19
19
|
projectTemplateDate?: string | undefined;
|
|
20
20
|
features?: {
|
|
@@ -33,16 +33,14 @@ export declare const defineConfigRse: (userConfig?: Partial<RseConfig>) => {
|
|
|
33
33
|
themes?: string[] | undefined;
|
|
34
34
|
} | undefined;
|
|
35
35
|
preferredLibraries?: {
|
|
36
|
-
search?: "unknown" | "algolia" | undefined;
|
|
37
|
-
cdn?: "unknown" | "cloudflare" | undefined;
|
|
38
36
|
i18n?: "unknown" | "next-intl" | undefined;
|
|
39
37
|
analytics?: "unknown" | "vercel" | undefined;
|
|
40
38
|
authentication?: "unknown" | "better-auth" | "clerk" | "next-auth" | "supabase-auth" | "auth0" | undefined;
|
|
41
|
-
api?: "
|
|
42
|
-
testing?: "
|
|
39
|
+
api?: "unknown" | "hono" | "trpc" | "graphql" | "rest" | undefined;
|
|
40
|
+
testing?: "unknown" | "bun" | "vitest" | "jest" | "playwright" | "cypress" | undefined;
|
|
43
41
|
stateManagement?: "unknown" | "zustand" | "jotai" | "redux-toolkit" | undefined;
|
|
44
42
|
formManagement?: "unknown" | "react-hook-form" | "formik" | undefined;
|
|
45
|
-
styling?: "
|
|
43
|
+
styling?: "unknown" | "tailwind" | "styled-components" | "css-modules" | "sass" | undefined;
|
|
46
44
|
uiComponents?: "unknown" | "shadcn-ui" | "chakra-ui" | "material-ui" | undefined;
|
|
47
45
|
databaseLibrary?: "unknown" | "drizzle" | "prisma" | "supabase" | undefined;
|
|
48
46
|
databaseProvider?: "unknown" | "pg" | "mysql" | "sqlite" | "mongodb" | undefined;
|
|
@@ -53,6 +51,7 @@ export declare const defineConfigRse: (userConfig?: Partial<RseConfig>) => {
|
|
|
53
51
|
logging?: "unknown" | "axiom" | undefined;
|
|
54
52
|
forms?: "unknown" | "react-hook-form" | undefined;
|
|
55
53
|
notifications?: "unknown" | "sonner" | undefined;
|
|
54
|
+
search?: "unknown" | "algolia" | undefined;
|
|
56
55
|
uploads?: "unknown" | "uploadthing" | undefined;
|
|
57
56
|
validation?: "unknown" | "zod" | "typebox" | "valibot" | undefined;
|
|
58
57
|
documentation?: "unknown" | "starlight" | "nextra" | undefined;
|
|
@@ -60,6 +59,7 @@ export declare const defineConfigRse: (userConfig?: Partial<RseConfig>) => {
|
|
|
60
59
|
mail?: "unknown" | "resend" | undefined;
|
|
61
60
|
cache?: "unknown" | "redis" | undefined;
|
|
62
61
|
storage?: "unknown" | "cloudflare" | undefined;
|
|
62
|
+
cdn?: "unknown" | "cloudflare" | undefined;
|
|
63
63
|
cms?: "unknown" | "contentlayer" | undefined;
|
|
64
64
|
seo?: "unknown" | "next-seo" | undefined;
|
|
65
65
|
motion?: "unknown" | "framer" | undefined;
|
|
@@ -75,7 +75,7 @@ export declare const defineConfigRse: (userConfig?: Partial<RseConfig>) => {
|
|
|
75
75
|
indentStyle?: "space" | "tab" | undefined;
|
|
76
76
|
quoteMark?: "single" | "double" | undefined;
|
|
77
77
|
semicolons?: boolean | undefined;
|
|
78
|
-
trailingComma?: "none" | "
|
|
78
|
+
trailingComma?: "none" | "es5" | "all" | undefined;
|
|
79
79
|
bracketSpacing?: boolean | undefined;
|
|
80
80
|
arrowParens?: "always" | "avoid" | undefined;
|
|
81
81
|
tabWidth?: number | undefined;
|
|
@@ -96,7 +96,7 @@ export declare const defineConfigRse: (userConfig?: Partial<RseConfig>) => {
|
|
|
96
96
|
importSymbol?: string | undefined;
|
|
97
97
|
} | undefined;
|
|
98
98
|
monorepo?: {
|
|
99
|
-
type?: "none" | "
|
|
99
|
+
type?: "none" | "turborepo" | "nx" | "pnpm" | "bun" | undefined;
|
|
100
100
|
packages?: string[] | undefined;
|
|
101
101
|
sharedPackages?: string[] | undefined;
|
|
102
102
|
} | undefined;
|
|
@@ -75,10 +75,6 @@ export type { CommentStyle, FileExtension, CommentMapping } from "./sdk-impl/uti
|
|
|
75
75
|
export { DEFAULT_COMMENT, COMMENT_MAP, getCommentPrefix } from "./sdk-impl/utils/comments.js";
|
|
76
76
|
export { detectFileType, detectBufferType, detectStreamType, isBinary, getMimeType, } from "./sdk-impl/utils/file-type.js";
|
|
77
77
|
export { finalizeBuild, finalizePub } from "./sdk-impl/utils/finalize.js";
|
|
78
|
-
export { WHITELABEL_DEFAULT, JSON_EXTS, TEMPLATE_VAR, TPLS_DIR, BINARIES_DIR, isJsonExt, } from "./sdk-impl/utils/pack-unpack/pu-constants.js";
|
|
79
|
-
export { walkDir, detectFileTypePU, readFileForTemplate, escapeTemplateString, } from "./sdk-impl/utils/pack-unpack/pu-file-utils.js";
|
|
80
|
-
export type { FileContent, FileType, FileMetadata, TemplatesFileContent, TemplateConfig, Template, Templates, } from "./sdk-impl/utils/pack-unpack/pu-types.js";
|
|
81
|
-
export { extractJsonComments, stripComments } from "./sdk-impl/utils/pack-unpack/pub-json-utils.js";
|
|
82
78
|
export { resolveCrossLibs, resolveAllCrossLibs } from "./sdk-impl/utils/resolve-cross-libs.js";
|
|
83
79
|
export { useAggregator } from "./sdk-impl/utils/tools-agg.js";
|
|
84
80
|
export { printUsage } from "./sdk-impl/utils/tools-impl.js";
|
package/bin/libs/sdk/sdk-mod.js
CHANGED
|
@@ -164,21 +164,6 @@ export {
|
|
|
164
164
|
getMimeType
|
|
165
165
|
} from "./sdk-impl/utils/file-type.js";
|
|
166
166
|
export { finalizeBuild, finalizePub } from "./sdk-impl/utils/finalize.js";
|
|
167
|
-
export {
|
|
168
|
-
WHITELABEL_DEFAULT,
|
|
169
|
-
JSON_EXTS,
|
|
170
|
-
TEMPLATE_VAR,
|
|
171
|
-
TPLS_DIR,
|
|
172
|
-
BINARIES_DIR,
|
|
173
|
-
isJsonExt
|
|
174
|
-
} from "./sdk-impl/utils/pack-unpack/pu-constants.js";
|
|
175
|
-
export {
|
|
176
|
-
walkDir,
|
|
177
|
-
detectFileTypePU,
|
|
178
|
-
readFileForTemplate,
|
|
179
|
-
escapeTemplateString
|
|
180
|
-
} from "./sdk-impl/utils/pack-unpack/pu-file-utils.js";
|
|
181
|
-
export { extractJsonComments, stripComments } from "./sdk-impl/utils/pack-unpack/pub-json-utils.js";
|
|
182
167
|
export { resolveCrossLibs, resolveAllCrossLibs } from "./sdk-impl/utils/resolve-cross-libs.js";
|
|
183
168
|
export { useAggregator } from "./sdk-impl/utils/tools-agg.js";
|
|
184
169
|
export { printUsage } from "./sdk-impl/utils/tools-impl.js";
|
package/package.json
CHANGED
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"rollup-plugin-dts": "^6.2.1",
|
|
45
45
|
"scule": "^1.3.0",
|
|
46
46
|
"semver": "^7.7.2",
|
|
47
|
+
"strip-json-comments": "^5.0.2",
|
|
47
48
|
"tinyglobby": "^0.2.14",
|
|
48
49
|
"ts-morph": "^26.0.0",
|
|
49
50
|
"untyped": "^2.0.0"
|
|
@@ -53,7 +54,7 @@
|
|
|
53
54
|
"license": "MIT",
|
|
54
55
|
"name": "@reliverse/dler",
|
|
55
56
|
"type": "module",
|
|
56
|
-
"version": "1.7.
|
|
57
|
+
"version": "1.7.51",
|
|
57
58
|
"keywords": [
|
|
58
59
|
"reliverse",
|
|
59
60
|
"cli",
|
package/bin/app/unpack/cmd.d.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
declare const _default: import("@reliverse/rempts").Command<{
|
|
2
|
-
templatesDir: {
|
|
3
|
-
type: "positional";
|
|
4
|
-
required: true;
|
|
5
|
-
description: string;
|
|
6
|
-
};
|
|
7
|
-
output: {
|
|
8
|
-
type: "string";
|
|
9
|
-
default: string;
|
|
10
|
-
description: string;
|
|
11
|
-
};
|
|
12
|
-
cdn: {
|
|
13
|
-
type: "string";
|
|
14
|
-
description: string;
|
|
15
|
-
};
|
|
16
|
-
deleteTemplates: {
|
|
17
|
-
type: "boolean";
|
|
18
|
-
description: string;
|
|
19
|
-
default: false;
|
|
20
|
-
};
|
|
21
|
-
"dry-run": {
|
|
22
|
-
type: "boolean";
|
|
23
|
-
description: string;
|
|
24
|
-
default: false;
|
|
25
|
-
};
|
|
26
|
-
force: {
|
|
27
|
-
type: "boolean";
|
|
28
|
-
description: string;
|
|
29
|
-
default: false;
|
|
30
|
-
};
|
|
31
|
-
cleanup: {
|
|
32
|
-
type: "boolean";
|
|
33
|
-
description: string;
|
|
34
|
-
default: false;
|
|
35
|
-
};
|
|
36
|
-
}>;
|
|
37
|
-
export default _default;
|
package/bin/app/unpack/cmd.js
DELETED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import path from "@reliverse/pathkit";
|
|
2
|
-
import { relinka } from "@reliverse/relinka";
|
|
3
|
-
import { defineArgs, defineCommand } from "@reliverse/rempts";
|
|
4
|
-
import { createJiti } from "jiti";
|
|
5
|
-
import { promises as fs } from "node:fs";
|
|
6
|
-
import { TPLS_DIR, BINARIES_DIR } from "../../libs/sdk/sdk-impl/utils/pack-unpack/pu-constants.js";
|
|
7
|
-
const jiti = createJiti(import.meta.url);
|
|
8
|
-
async function removeEmptyDirs(dirPath) {
|
|
9
|
-
if (!await fs.access(dirPath).then(() => true).catch(() => false))
|
|
10
|
-
return;
|
|
11
|
-
const entries = await fs.readdir(dirPath);
|
|
12
|
-
if (entries.length === 0) {
|
|
13
|
-
await fs.rmdir(dirPath);
|
|
14
|
-
const parentDir = path.dirname(dirPath);
|
|
15
|
-
if (parentDir !== dirPath) {
|
|
16
|
-
await removeEmptyDirs(parentDir);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
async function deleteTemplates(templatesDir, templatesDirName, dryRun) {
|
|
21
|
-
const modFile = `${templatesDirName}-mod.ts`;
|
|
22
|
-
const typesFile = `${templatesDirName}-types.ts`;
|
|
23
|
-
const implDir = path.join(templatesDir, TPLS_DIR);
|
|
24
|
-
const binariesDir = path.join(implDir, BINARIES_DIR);
|
|
25
|
-
const filesToRemove = [path.join(templatesDir, modFile), path.join(templatesDir, typesFile)];
|
|
26
|
-
try {
|
|
27
|
-
const templateFiles = await fs.readdir(implDir);
|
|
28
|
-
for (const file of templateFiles) {
|
|
29
|
-
if (file.endsWith(".ts")) {
|
|
30
|
-
filesToRemove.push(path.join(implDir, file));
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
} catch (error) {
|
|
34
|
-
if (error.code !== "ENOENT") {
|
|
35
|
-
throw error;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
for (const file of filesToRemove) {
|
|
39
|
-
if (dryRun) {
|
|
40
|
-
relinka("log", `[DRY RUN] Would remove: ${file}`);
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
try {
|
|
44
|
-
await fs.unlink(file);
|
|
45
|
-
relinka("log", `Removed: ${file}`);
|
|
46
|
-
} catch (error) {
|
|
47
|
-
if (error.code !== "ENOENT") {
|
|
48
|
-
relinka("warn", `Failed to remove ${file}: ${error.message}`);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
try {
|
|
53
|
-
if (await fs.access(binariesDir).then(() => true).catch(() => false)) {
|
|
54
|
-
if (dryRun) {
|
|
55
|
-
relinka("log", `[DRY RUN] Would remove directory: ${binariesDir}`);
|
|
56
|
-
} else {
|
|
57
|
-
await fs.rm(binariesDir, { recursive: true });
|
|
58
|
-
relinka("log", `Removed directory: ${binariesDir}`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
} catch (error) {
|
|
62
|
-
relinka("warn", `Failed to remove binaries directory: ${error.message}`);
|
|
63
|
-
}
|
|
64
|
-
await removeEmptyDirs(implDir);
|
|
65
|
-
await removeEmptyDirs(templatesDir);
|
|
66
|
-
}
|
|
67
|
-
export default defineCommand({
|
|
68
|
-
meta: {
|
|
69
|
-
name: "unpack",
|
|
70
|
-
version: "1.1.0",
|
|
71
|
-
description: "Creates file structure from packed templates"
|
|
72
|
-
},
|
|
73
|
-
args: defineArgs({
|
|
74
|
-
templatesDir: { type: "positional", required: true, description: "Dir containing *-mod.ts" },
|
|
75
|
-
output: { type: "string", default: "unpacked", description: "Where to write files" },
|
|
76
|
-
cdn: {
|
|
77
|
-
type: "string",
|
|
78
|
-
description: "Remote CDN base for binary assets download (not yet implemented)"
|
|
79
|
-
},
|
|
80
|
-
deleteTemplates: {
|
|
81
|
-
type: "boolean",
|
|
82
|
-
description: "Delete template implementation files (*-mod.ts, *-types.ts, etc.)",
|
|
83
|
-
default: false
|
|
84
|
-
},
|
|
85
|
-
"dry-run": {
|
|
86
|
-
type: "boolean",
|
|
87
|
-
description: "Preview changes without applying them",
|
|
88
|
-
default: false
|
|
89
|
-
},
|
|
90
|
-
force: {
|
|
91
|
-
type: "boolean",
|
|
92
|
-
description: "Force overwrite existing files",
|
|
93
|
-
default: false
|
|
94
|
-
},
|
|
95
|
-
cleanup: {
|
|
96
|
-
type: "boolean",
|
|
97
|
-
description: "Delete output directory before unpacking",
|
|
98
|
-
default: false
|
|
99
|
-
}
|
|
100
|
-
}),
|
|
101
|
-
async run({ args }) {
|
|
102
|
-
if (args.cdn) throw new Error("Remote CDN support is not implemented yet.");
|
|
103
|
-
const templatesDir = path.resolve(args.templatesDir);
|
|
104
|
-
const templatesDirName = path.basename(templatesDir);
|
|
105
|
-
const modFile = `${templatesDirName}-mod.ts`;
|
|
106
|
-
const modPath = path.join(templatesDir, modFile);
|
|
107
|
-
if (args.deleteTemplates) {
|
|
108
|
-
await deleteTemplates(templatesDir, templatesDirName, args["dry-run"]);
|
|
109
|
-
if (args["dry-run"]) {
|
|
110
|
-
relinka("log", "[DRY RUN] Templates deletion completed");
|
|
111
|
-
} else {
|
|
112
|
-
relinka("log", "Templates deletion completed");
|
|
113
|
-
}
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
if (args.cleanup) {
|
|
117
|
-
try {
|
|
118
|
-
await fs.rm(args.output, { recursive: true, force: true });
|
|
119
|
-
await fs.mkdir(args.output, { recursive: true });
|
|
120
|
-
relinka("log", `Cleaned output directory: ${args.output}`);
|
|
121
|
-
} catch (error) {
|
|
122
|
-
if (error.code !== "ENOENT") {
|
|
123
|
-
throw error;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
const mod = await jiti.import(modPath);
|
|
128
|
-
const templatesObj = mod?.DLER_TEMPLATES || mod?.default || (() => {
|
|
129
|
-
throw new Error(`Invalid ${modFile}`);
|
|
130
|
-
})();
|
|
131
|
-
for (const tpl of Object.values(templatesObj)) {
|
|
132
|
-
await restoreTemplate(tpl, templatesDir, args.output, args.force);
|
|
133
|
-
}
|
|
134
|
-
const relativeOutput = path.relative(process.cwd(), args.output);
|
|
135
|
-
relinka(
|
|
136
|
-
"log",
|
|
137
|
-
`Unpacked ${Object.keys(templatesObj).length} templates from ${modFile} into ${relativeOutput}${args.force ? " (--force is true, dir was overwritten)" : ""}`
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
const restoreTemplate = async (tpl, templatesRoot, outRoot, force) => {
|
|
142
|
-
const existingFiles = [];
|
|
143
|
-
for (const [rel, meta] of Object.entries(tpl.config.files)) {
|
|
144
|
-
const destAbs = path.join(outRoot, rel);
|
|
145
|
-
if (!force) {
|
|
146
|
-
try {
|
|
147
|
-
await fs.access(destAbs);
|
|
148
|
-
existingFiles.push(rel);
|
|
149
|
-
continue;
|
|
150
|
-
} catch {
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
await fs.mkdir(path.dirname(destAbs), { recursive: true });
|
|
154
|
-
switch (meta.type) {
|
|
155
|
-
case "binary": {
|
|
156
|
-
if (!meta.binaryHash) {
|
|
157
|
-
await fs.writeFile(destAbs, "");
|
|
158
|
-
break;
|
|
159
|
-
}
|
|
160
|
-
const ext = path.extname(rel);
|
|
161
|
-
const binPath = path.join(
|
|
162
|
-
templatesRoot,
|
|
163
|
-
TPLS_DIR,
|
|
164
|
-
BINARIES_DIR,
|
|
165
|
-
`${meta.binaryHash}${ext}`
|
|
166
|
-
);
|
|
167
|
-
await fs.copyFile(binPath, destAbs);
|
|
168
|
-
break;
|
|
169
|
-
}
|
|
170
|
-
case "text": {
|
|
171
|
-
await fs.writeFile(destAbs, meta.content, "utf8");
|
|
172
|
-
break;
|
|
173
|
-
}
|
|
174
|
-
case "json": {
|
|
175
|
-
const txt = JSON.stringify(meta.content, null, 2);
|
|
176
|
-
const withComments = meta.jsonComments ? injectComments(txt, meta.jsonComments) : txt;
|
|
177
|
-
await fs.writeFile(destAbs, withComments, "utf8");
|
|
178
|
-
break;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
if (existingFiles.length > 0) {
|
|
183
|
-
throw new Error(
|
|
184
|
-
`Cannot unpack: ${existingFiles.length} file(s) already exist. Use --force to overwrite existing files`
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
const injectComments = (json, comments) => {
|
|
189
|
-
const lines = json.split("\n");
|
|
190
|
-
const entries = Object.entries(comments).map(([k, v]) => [Number(k), v]).sort((a, b) => a[0] - b[0]);
|
|
191
|
-
let offset = 0;
|
|
192
|
-
for (const [ln, comment] of entries) {
|
|
193
|
-
const idx = ln - 1 + offset;
|
|
194
|
-
const indent = lines[idx]?.match(/^\s*/)?.[0] ?? "";
|
|
195
|
-
const block = comment.split("\n").map((l) => indent + l);
|
|
196
|
-
lines.splice(idx, 0, ...block);
|
|
197
|
-
offset += block.length;
|
|
198
|
-
}
|
|
199
|
-
return lines.join("\n") + "\n";
|
|
200
|
-
};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export declare const WHITELABEL_DEFAULT = "DLER";
|
|
2
|
-
export declare const JSON_EXTS: Set<string>;
|
|
3
|
-
export declare const TEMPLATE_VAR: (tpl: string, wl: string) => string;
|
|
4
|
-
export declare const TPLS_DIR = "tpls";
|
|
5
|
-
export declare const BINARIES_DIR = "binaries";
|
|
6
|
-
export declare const isJsonExt: (f: string) => boolean;
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import path from "@reliverse/pathkit";
|
|
2
|
-
export const WHITELABEL_DEFAULT = "DLER";
|
|
3
|
-
export const JSON_EXTS = /* @__PURE__ */ new Set(["json", "jsonc", "json5", "jsonl"]);
|
|
4
|
-
export const TEMPLATE_VAR = (tpl, wl) => `${tpl.toUpperCase()}_${wl.toUpperCase()}_TEMPLATE`;
|
|
5
|
-
export const TPLS_DIR = "tpls";
|
|
6
|
-
export const BINARIES_DIR = "binaries";
|
|
7
|
-
export const isJsonExt = (f) => JSON_EXTS.has(path.extname(f).slice(1).toLowerCase());
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { FileType, TemplatesFileContent } from "./pu-types";
|
|
2
|
-
export declare const walkDir: (dir: string) => Promise<string[]>;
|
|
3
|
-
export declare const detectFileTypePU: (file: string) => Promise<FileType>;
|
|
4
|
-
export declare const readFileForTemplate: (absPath: string) => Promise<TemplatesFileContent>;
|
|
5
|
-
export declare const escapeTemplateString: (src: string) => string;
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import path from "@reliverse/pathkit";
|
|
2
|
-
import { promises as fs } from "node:fs";
|
|
3
|
-
import { isBinaryExt } from "../binary.js";
|
|
4
|
-
import { isJsonExt } from "./pu-constants.js";
|
|
5
|
-
import { extractJsonComments, stripComments } from "./pub-json-utils.js";
|
|
6
|
-
export const walkDir = async (dir) => {
|
|
7
|
-
const entries = [];
|
|
8
|
-
for (const entry of await fs.readdir(dir, { withFileTypes: true })) {
|
|
9
|
-
const full = path.join(dir, entry.name);
|
|
10
|
-
if (entry.isDirectory()) {
|
|
11
|
-
entries.push(...await walkDir(full));
|
|
12
|
-
} else {
|
|
13
|
-
entries.push(full);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
return entries;
|
|
17
|
-
};
|
|
18
|
-
export const detectFileTypePU = async (file) => {
|
|
19
|
-
if (await isBinaryExt(file)) return "binary";
|
|
20
|
-
if (isJsonExt(file)) return "json";
|
|
21
|
-
return "text";
|
|
22
|
-
};
|
|
23
|
-
export const readFileForTemplate = async (absPath) => {
|
|
24
|
-
const type = await detectFileTypePU(absPath);
|
|
25
|
-
try {
|
|
26
|
-
if (type === "binary") {
|
|
27
|
-
return { type, content: "" };
|
|
28
|
-
}
|
|
29
|
-
const data = await fs.readFile(absPath, "utf8");
|
|
30
|
-
if (type === "json") {
|
|
31
|
-
const jsonComments = extractJsonComments(data);
|
|
32
|
-
const json = JSON.parse(stripComments(data));
|
|
33
|
-
return { type, content: json, jsonComments };
|
|
34
|
-
}
|
|
35
|
-
return { type, content: data };
|
|
36
|
-
} catch {
|
|
37
|
-
return { type, content: "", hasError: true };
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
export const escapeTemplateString = (src) => src.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export type FileContent = string | Record<string, unknown>;
|
|
2
|
-
export type FileType = "text" | "json" | "binary";
|
|
3
|
-
export interface FileMetadata {
|
|
4
|
-
updatedAt: string;
|
|
5
|
-
updatedHash: string;
|
|
6
|
-
}
|
|
7
|
-
export interface TemplatesFileContent {
|
|
8
|
-
content: FileContent;
|
|
9
|
-
type: FileType;
|
|
10
|
-
hasError?: boolean;
|
|
11
|
-
jsonComments?: Record<number, string>;
|
|
12
|
-
binaryHash?: string;
|
|
13
|
-
metadata?: FileMetadata;
|
|
14
|
-
}
|
|
15
|
-
export interface TemplateConfig {
|
|
16
|
-
files: Record<string, TemplatesFileContent>;
|
|
17
|
-
}
|
|
18
|
-
export interface Template {
|
|
19
|
-
name: string;
|
|
20
|
-
description: string;
|
|
21
|
-
config: TemplateConfig;
|
|
22
|
-
updatedAt: string;
|
|
23
|
-
}
|
|
24
|
-
export type Templates = Record<string, Template>;
|
|
File without changes
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Helpers for reading / writing JSON-with-comments files while
|
|
3
|
-
* preserving (or later reinjecting) on–line and block comments.
|
|
4
|
-
*
|
|
5
|
-
* Supported comment styles:
|
|
6
|
-
* // single-line
|
|
7
|
-
* /* … *\/
|
|
8
|
-
* /** … *\/
|
|
9
|
-
*/
|
|
10
|
-
export declare const extractJsonComments: (raw: string) => Record<number, string> | undefined;
|
|
11
|
-
/**
|
|
12
|
-
* Very small "strip comments" helper:
|
|
13
|
-
* removes // … and /* … *\/ (including multi-line).
|
|
14
|
-
* This is intentionally simple; for complex JSONC we may want
|
|
15
|
-
* use something like `strip-json-comments` in the future.
|
|
16
|
-
*/
|
|
17
|
-
export declare const stripComments: (raw: string) => string;
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
export const extractJsonComments = (raw) => {
|
|
2
|
-
const out = {};
|
|
3
|
-
const lines = raw.split("\n");
|
|
4
|
-
for (let i = 0; i < lines.length; ) {
|
|
5
|
-
const line = lines[i];
|
|
6
|
-
if (!line) {
|
|
7
|
-
i += 1;
|
|
8
|
-
continue;
|
|
9
|
-
}
|
|
10
|
-
const trimmed = line.trimStart();
|
|
11
|
-
const indent = line.length - trimmed.length;
|
|
12
|
-
if (trimmed.startsWith("//")) {
|
|
13
|
-
out[i + 1] = trimmed.slice(2).trimStart();
|
|
14
|
-
i += 1;
|
|
15
|
-
continue;
|
|
16
|
-
}
|
|
17
|
-
if (trimmed.startsWith("/*")) {
|
|
18
|
-
const isSupported = trimmed.startsWith("/**") || trimmed.startsWith("/*");
|
|
19
|
-
if (!isSupported) {
|
|
20
|
-
i += 1;
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
const buff = [trimmed];
|
|
24
|
-
let j = i + 1;
|
|
25
|
-
while (j < lines.length) {
|
|
26
|
-
const currentLine = lines[j];
|
|
27
|
-
if (!currentLine) {
|
|
28
|
-
j += 1;
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
if (currentLine.trimEnd().endsWith("*/")) {
|
|
32
|
-
buff.push(currentLine.slice(indent));
|
|
33
|
-
break;
|
|
34
|
-
}
|
|
35
|
-
buff.push(currentLine.slice(indent));
|
|
36
|
-
j += 1;
|
|
37
|
-
}
|
|
38
|
-
out[i + 1] = buff.join("\n");
|
|
39
|
-
i = j + 1;
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
i += 1;
|
|
43
|
-
}
|
|
44
|
-
return Object.keys(out).length ? out : void 0;
|
|
45
|
-
};
|
|
46
|
-
export const stripComments = (raw) => raw.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/[^\r\n]*/g, "");
|