@cmtlyt/unplugin-shadcn-registry-generate 0.1.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 +52 -0
- package/configuration-schema.json +23 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +391 -0
- package/dist/types/index.d.ts +38 -0
- package/dist/utils/base.d.ts +3 -0
- package/dist/utils/context.d.ts +14 -0
- package/dist/utils/generate-exports/clear-exports-fields.d.ts +2 -0
- package/dist/utils/generate-exports/get-file.d.ts +2 -0
- package/dist/utils/generate-exports/index.d.ts +3 -0
- package/dist/utils/generate-exports/patch-file-and-deps.d.ts +2 -0
- package/dist/utils/generate-exports/registry-item-optimization.d.ts +3 -0
- package/dist/utils/generate-shadcn-registry/index.d.ts +2 -0
- package/dist/utils/generate-shadcn-registry/registry-item.d.ts +2 -0
- package/dist/utils/generate-shadcn-registry/registry.d.ts +2 -0
- package/dist/utils/get-name-from-path.d.ts +2 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/require.d.ts +1 -0
- package/dist/utils/ts-paths-resolve.d.ts +14 -0
- package/dist/utils/verify.d.ts +4 -0
- package/package.json +66 -0
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# unplugin-shadcn-registry-generate
|
|
2
|
+
|
|
3
|
+
生成 shadcn 的 registry 和对应的 registry-item
|
|
4
|
+
|
|
5
|
+
## 使用
|
|
6
|
+
|
|
7
|
+
创建 `shadcn-exports.json` 文件, 示例内容如下
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"$schema": "./node_modules/@cmtlyt/unplugin-shadcn-registry-generate",
|
|
12
|
+
"exports": [
|
|
13
|
+
{ "path": "./src/atomic-functions/assert-never.ts" },
|
|
14
|
+
{ "path": "./src/atomic-functions/sleep.ts" },
|
|
15
|
+
{ "path": "./src/tools/allx.ts", "name": "promiseAllX", "description": "promiseAllX", "title": "promiseAllX" },
|
|
16
|
+
{ "path": "./src/types/base.ts" }
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
在打包工具配置文件中补充如下内容, 以 rslib 为例
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { defineConfig } from '@rslib/core';
|
|
25
|
+
import shadcnRegistryGenerator from '@cmtlyt/unplugin-shadcn-registry-generate';
|
|
26
|
+
|
|
27
|
+
export default defineConfig({
|
|
28
|
+
lib: [
|
|
29
|
+
{
|
|
30
|
+
format: 'esm',
|
|
31
|
+
syntax: ['node 18'],
|
|
32
|
+
dts: true,
|
|
33
|
+
tools: {
|
|
34
|
+
rspack: {
|
|
35
|
+
plugins: [shadcnRegistryGenerator.rspack()]
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
然后执行 `npm run build` 即可
|
|
44
|
+
|
|
45
|
+
## 提示
|
|
46
|
+
|
|
47
|
+
name 生成规则如下
|
|
48
|
+
|
|
49
|
+
- 文件名不为 index 使用文件名
|
|
50
|
+
- 文件夹名
|
|
51
|
+
|
|
52
|
+
所以 name 是可能会冲突的, 为了更好的控制 name 最好是手动配置 name
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"exports": {
|
|
6
|
+
"type": "array",
|
|
7
|
+
"items": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {
|
|
10
|
+
"name": { "type": "string" },
|
|
11
|
+
"path": { "type": "string" },
|
|
12
|
+
"description": { "type": "string" },
|
|
13
|
+
"title": { "type": "string" },
|
|
14
|
+
"author": { "type": "string" }
|
|
15
|
+
},
|
|
16
|
+
"required": ["path"]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"required": ["exports"],
|
|
21
|
+
"uniqueItems": true,
|
|
22
|
+
"minItems": 1
|
|
23
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import { createUnplugin } from "unplugin";
|
|
2
|
+
import node_fs from "node:fs";
|
|
3
|
+
import node_path from "node:path";
|
|
4
|
+
import { readTSConfig, resolvePackageJSON } from "pkg-types";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
6
|
+
import { randomUUID } from "node:crypto";
|
|
7
|
+
function initOuputDir(ctx) {
|
|
8
|
+
const { options } = ctx;
|
|
9
|
+
const { outputDir } = options;
|
|
10
|
+
if (!node_fs.existsSync(outputDir)) node_fs.mkdirSync(outputDir, {
|
|
11
|
+
recursive: true
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
function normalizeBasePath(basePath) {
|
|
15
|
+
if (basePath.endsWith('/')) return basePath;
|
|
16
|
+
return `${basePath}/`;
|
|
17
|
+
}
|
|
18
|
+
function normalizeOptions(options) {
|
|
19
|
+
if (!options.registryUrl) throw new Error('registryUrl is required');
|
|
20
|
+
return {
|
|
21
|
+
registryUrl: options.registryUrl,
|
|
22
|
+
outputDir: options.outputDir || './public/r',
|
|
23
|
+
basePath: normalizeBasePath(options.basePath || '~/'),
|
|
24
|
+
noRootRegistry: true === options.noRootRegistry
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function readConfig(ctx) {
|
|
28
|
+
const configPath = node_path.join(ctx.baseDir, 'shadcn-exports.json');
|
|
29
|
+
if (!node_fs.existsSync(configPath)) return null;
|
|
30
|
+
return JSON.parse(node_fs.readFileSync(configPath, 'utf-8'));
|
|
31
|
+
}
|
|
32
|
+
async function initContext(ctx) {
|
|
33
|
+
ctx.baseDir = node_path.dirname(await resolvePackageJSON());
|
|
34
|
+
const config = await readConfig(ctx);
|
|
35
|
+
if (!config) console.warn('shadcn-exports.json not found');
|
|
36
|
+
ctx.config = config || {
|
|
37
|
+
exports: []
|
|
38
|
+
};
|
|
39
|
+
ctx.tsConfig = await readTSConfig(ctx.baseDir);
|
|
40
|
+
}
|
|
41
|
+
function createContext(options) {
|
|
42
|
+
return {
|
|
43
|
+
options: normalizeOptions(options),
|
|
44
|
+
baseDir: '',
|
|
45
|
+
config: null,
|
|
46
|
+
exports: null,
|
|
47
|
+
tsConfig: null,
|
|
48
|
+
runCtx: {
|
|
49
|
+
reqistrys: [],
|
|
50
|
+
dependenciesSet: null,
|
|
51
|
+
aliasMap: {}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function getNameFromPath(exportItem, ctx) {
|
|
56
|
+
if (node_fs.statSync(exportItem.path).isFile()) {
|
|
57
|
+
const baseName = node_path.basename(exportItem.path);
|
|
58
|
+
if (baseName.startsWith('index')) return node_path.basename(node_path.dirname(exportItem.path), ctx.baseDir);
|
|
59
|
+
return baseName.replace(/\.\w+$/, '');
|
|
60
|
+
}
|
|
61
|
+
return node_path.basename(exportItem.path, ctx.baseDir);
|
|
62
|
+
}
|
|
63
|
+
const fileRequire = createRequire(import.meta.url);
|
|
64
|
+
function isNodeOrgModule(id) {
|
|
65
|
+
return id.startsWith('node:') || !id.includes('/');
|
|
66
|
+
}
|
|
67
|
+
function pathMatch(realPath, id, isFile) {
|
|
68
|
+
if (isFile) return realPath === id;
|
|
69
|
+
return id.startsWith(realPath) && !id.includes('node_modules');
|
|
70
|
+
}
|
|
71
|
+
function fileInWorkspace(filePath, ctx) {
|
|
72
|
+
return pathMatch(ctx.baseDir, filePath, false);
|
|
73
|
+
}
|
|
74
|
+
function extTry(filePath, _ctx) {
|
|
75
|
+
const exts = [
|
|
76
|
+
'.ts',
|
|
77
|
+
'.tsx',
|
|
78
|
+
'.js',
|
|
79
|
+
'.jsx',
|
|
80
|
+
'.cjs',
|
|
81
|
+
'.mjs',
|
|
82
|
+
'.vue'
|
|
83
|
+
];
|
|
84
|
+
for(let i = 0; i < exts.length; i++){
|
|
85
|
+
const ext = exts[i];
|
|
86
|
+
const fullPath = `${filePath}${ext}`;
|
|
87
|
+
if (node_fs.existsSync(fullPath)) return fullPath;
|
|
88
|
+
}
|
|
89
|
+
if (node_fs.existsSync(`${filePath}/index.ts`)) return `${filePath}/index.ts`;
|
|
90
|
+
return filePath;
|
|
91
|
+
}
|
|
92
|
+
function tsPathsResolve(id, ctx, filePath) {
|
|
93
|
+
try {
|
|
94
|
+
const tempId = fileRequire.resolve(id);
|
|
95
|
+
if (node_path.isAbsolute(tempId)) return {
|
|
96
|
+
type: 'original',
|
|
97
|
+
originalId: id,
|
|
98
|
+
resolvedId: id
|
|
99
|
+
};
|
|
100
|
+
} catch {}
|
|
101
|
+
if (isNodeOrgModule(id)) return {
|
|
102
|
+
type: 'original',
|
|
103
|
+
originalId: id,
|
|
104
|
+
resolvedId: id
|
|
105
|
+
};
|
|
106
|
+
const { paths, baseUrl } = ctx.tsConfig?.compilerOptions || {};
|
|
107
|
+
const fileDir = node_path.dirname(filePath);
|
|
108
|
+
if (!paths) return {
|
|
109
|
+
type: 'relative',
|
|
110
|
+
originalId: id,
|
|
111
|
+
resolvedId: extTry(node_path.resolve(fileDir, id), ctx)
|
|
112
|
+
};
|
|
113
|
+
const keys = Reflect.ownKeys(paths);
|
|
114
|
+
for(let i = 0; i < keys.length; i++){
|
|
115
|
+
const key = keys[i];
|
|
116
|
+
const value = paths[key][0].replace(/\*$/, '');
|
|
117
|
+
if (id === key) {
|
|
118
|
+
const resolvedId = extTry(node_path.resolve(baseUrl || ctx.baseDir, value), ctx);
|
|
119
|
+
return {
|
|
120
|
+
type: 'alias',
|
|
121
|
+
originalId: id,
|
|
122
|
+
resolvedId,
|
|
123
|
+
relativePath: node_path.relative(fileDir, resolvedId),
|
|
124
|
+
aliasPath: value
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const keyWithoutStar = key.replace(/\*$/, '');
|
|
128
|
+
if (id.startsWith(keyWithoutStar)) {
|
|
129
|
+
const resolvedId = extTry(node_path.resolve(baseUrl || ctx.baseDir, id.replace(keyWithoutStar, value)), ctx);
|
|
130
|
+
return {
|
|
131
|
+
type: 'alias',
|
|
132
|
+
originalId: id,
|
|
133
|
+
resolvedId,
|
|
134
|
+
relativePath: node_path.relative(fileDir, resolvedId),
|
|
135
|
+
aliasPath: value
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
type: 'relative',
|
|
141
|
+
originalId: id,
|
|
142
|
+
resolvedId: extTry(node_path.resolve(fileDir, id), ctx)
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function getFileDependencies(filePath, fileContent, ctx, dependenciesSet, uuid) {
|
|
146
|
+
const dependencieRegexp = /import.*?from.*?(['"])(.*?)\1|import.*?(['"])(.*?)\3|import\(.*?(['"])(.*?)\5.*?\)|export.*?from.*?(['"])(.*?)\7/g;
|
|
147
|
+
const dependencies = [];
|
|
148
|
+
let match = dependencieRegexp.exec(fileContent);
|
|
149
|
+
while(null !== match){
|
|
150
|
+
const [, , id1, , id2, , id3, , id4] = match;
|
|
151
|
+
const id = id1 || id2 || id3 || id4;
|
|
152
|
+
try {
|
|
153
|
+
const { resolvedId, relativePath, type, ...resolveInfo } = tsPathsResolve(id, ctx, filePath);
|
|
154
|
+
if ('alias' === type && relativePath) ctx.runCtx.aliasMap[uuid].push({
|
|
155
|
+
...resolveInfo,
|
|
156
|
+
relativePath,
|
|
157
|
+
resolvedId
|
|
158
|
+
});
|
|
159
|
+
if (dependenciesSet.has(resolvedId)) {
|
|
160
|
+
match = dependencieRegexp.exec(fileContent);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const depPath = fileRequire.resolve(resolvedId);
|
|
164
|
+
dependencies.push(...getFile(depPath, ctx, id, fileInWorkspace(depPath, ctx)));
|
|
165
|
+
dependenciesSet.add(depPath);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
console.warn(error);
|
|
168
|
+
}
|
|
169
|
+
match = dependencieRegexp.exec(fileContent);
|
|
170
|
+
}
|
|
171
|
+
return dependencies;
|
|
172
|
+
}
|
|
173
|
+
function getFile(filePath, ctx, id, inWorkspace = true) {
|
|
174
|
+
const { runCtx, options } = ctx;
|
|
175
|
+
const { dependenciesSet } = runCtx;
|
|
176
|
+
if (dependenciesSet.has(filePath)) return [];
|
|
177
|
+
const relativePath = node_path.relative(ctx.baseDir, filePath);
|
|
178
|
+
const files = [];
|
|
179
|
+
const uuid = randomUUID();
|
|
180
|
+
const aliases = [];
|
|
181
|
+
ctx.runCtx.aliasMap[uuid] = aliases;
|
|
182
|
+
const fileContent = node_fs.readFileSync(filePath, 'utf-8');
|
|
183
|
+
if (inWorkspace) {
|
|
184
|
+
files.push({
|
|
185
|
+
uuid,
|
|
186
|
+
path: relativePath,
|
|
187
|
+
type: 'registry:file',
|
|
188
|
+
target: `${options.basePath}${node_path.relative(ctx.baseDir, filePath)}`,
|
|
189
|
+
fileType: 'workfile',
|
|
190
|
+
aliases,
|
|
191
|
+
fileContent
|
|
192
|
+
});
|
|
193
|
+
const deps = getFileDependencies(filePath, fileContent, ctx, dependenciesSet, uuid);
|
|
194
|
+
files.push(...deps);
|
|
195
|
+
} else {
|
|
196
|
+
const isDependency = filePath.includes('node_modules');
|
|
197
|
+
if (!isNodeOrgModule(filePath)) files.push({
|
|
198
|
+
uuid,
|
|
199
|
+
path: relativePath,
|
|
200
|
+
id,
|
|
201
|
+
fileType: isDependency ? 'dependency' : 'external',
|
|
202
|
+
aliases,
|
|
203
|
+
fileContent
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return files;
|
|
207
|
+
}
|
|
208
|
+
function readDirFiles(realPath, files, ctx) {
|
|
209
|
+
node_fs.readdirSync(realPath).forEach((file)=>{
|
|
210
|
+
const filePath = node_path.join(realPath, file);
|
|
211
|
+
const isFile = node_fs.statSync(filePath).isFile();
|
|
212
|
+
if (isFile) return void files.push(...getFile(filePath, ctx));
|
|
213
|
+
readDirFiles(filePath, files, ctx);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
function patchFileAndDeps(exportItem, ctx) {
|
|
217
|
+
const { extInfo, files, dependencies, registryDependencies } = exportItem;
|
|
218
|
+
ctx.runCtx.dependenciesSet = new Set();
|
|
219
|
+
if (extInfo.isFile) files.push(...getFile(exportItem.extInfo.realPath, ctx));
|
|
220
|
+
else readDirFiles(extInfo.realPath, files, ctx);
|
|
221
|
+
exportItem.files = files.filter((item)=>{
|
|
222
|
+
if ('workfile' === item.fileType) return true;
|
|
223
|
+
if ('registry' === item.fileType) {
|
|
224
|
+
registryDependencies.push(`./${item.id}.json`);
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
if ('dependency' === item.fileType) dependencies.push(item.id);
|
|
228
|
+
return false;
|
|
229
|
+
}).map((item)=>{
|
|
230
|
+
const { fileType: _, ...rest } = item;
|
|
231
|
+
return rest;
|
|
232
|
+
});
|
|
233
|
+
ctx.runCtx.dependenciesSet = null;
|
|
234
|
+
}
|
|
235
|
+
function resolveRegistryDependencieUrl(name, ctx) {
|
|
236
|
+
const { options } = ctx;
|
|
237
|
+
return `${options.registryUrl}/${name}.json`;
|
|
238
|
+
}
|
|
239
|
+
function registryItemOptimization(regItem, ctx) {
|
|
240
|
+
const { extInfo } = regItem;
|
|
241
|
+
const { fileMap } = extInfo;
|
|
242
|
+
const { reqistrys } = ctx.runCtx;
|
|
243
|
+
reqistrys.filter((item)=>{
|
|
244
|
+
if (item.name === regItem.name) return false;
|
|
245
|
+
if (item.extInfo.filePaths.some((filePath)=>!fileMap.has(filePath))) return false;
|
|
246
|
+
return true;
|
|
247
|
+
}).forEach((item)=>{
|
|
248
|
+
regItem.registryDependencies.push(resolveRegistryDependencieUrl(item.name, ctx));
|
|
249
|
+
item.extInfo.filePaths.forEach((filePath)=>{
|
|
250
|
+
fileMap.delete(filePath);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
const files = Array.from(fileMap.values());
|
|
254
|
+
return {
|
|
255
|
+
...regItem,
|
|
256
|
+
files
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
function clearExportsFields(ctx) {
|
|
260
|
+
const { exports } = ctx;
|
|
261
|
+
ctx.exports = exports.map((item)=>{
|
|
262
|
+
const { extInfo: _, ...otherItem } = item;
|
|
263
|
+
const files = otherItem.files.map((file)=>{
|
|
264
|
+
const { path: filePath, type, target } = file;
|
|
265
|
+
return {
|
|
266
|
+
path: filePath,
|
|
267
|
+
type,
|
|
268
|
+
target
|
|
269
|
+
};
|
|
270
|
+
});
|
|
271
|
+
return {
|
|
272
|
+
...otherItem,
|
|
273
|
+
files
|
|
274
|
+
};
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
function filesSummary(item) {
|
|
278
|
+
const files = item.files;
|
|
279
|
+
const fileMap = new Map();
|
|
280
|
+
files.forEach((file)=>{
|
|
281
|
+
const { path: filePath } = file;
|
|
282
|
+
fileMap.set(filePath, file);
|
|
283
|
+
});
|
|
284
|
+
item.extInfo.filePaths = Array.from(fileMap.keys());
|
|
285
|
+
item.extInfo.fileMap = fileMap;
|
|
286
|
+
}
|
|
287
|
+
function generateExports(ctx) {
|
|
288
|
+
const { config } = ctx;
|
|
289
|
+
const { exports: _exports } = config;
|
|
290
|
+
const exports = _exports.map((item)=>{
|
|
291
|
+
const { path: _path, ...rest } = item;
|
|
292
|
+
const name = rest.name || getNameFromPath(item, ctx);
|
|
293
|
+
const { resolvedId: realPath } = tsPathsResolve(item.path, ctx, `${ctx.baseDir}/package.json`);
|
|
294
|
+
const isFile = node_fs.statSync(realPath).isFile();
|
|
295
|
+
const resolveId = fileRequire.resolve(realPath);
|
|
296
|
+
const registryItem = {
|
|
297
|
+
title: name,
|
|
298
|
+
name: name,
|
|
299
|
+
description: name,
|
|
300
|
+
registryDependencies: [],
|
|
301
|
+
dependencies: [],
|
|
302
|
+
files: [],
|
|
303
|
+
...rest,
|
|
304
|
+
type: 'registry:item',
|
|
305
|
+
extInfo: {
|
|
306
|
+
realPath,
|
|
307
|
+
isFile,
|
|
308
|
+
resolveId
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
ctx.runCtx.reqistrys.push(registryItem);
|
|
312
|
+
return registryItem;
|
|
313
|
+
}).map((item)=>{
|
|
314
|
+
patchFileAndDeps(item, ctx);
|
|
315
|
+
filesSummary(item);
|
|
316
|
+
return item;
|
|
317
|
+
}).map((item)=>registryItemOptimization(item, ctx));
|
|
318
|
+
ctx.exports = exports;
|
|
319
|
+
return exports;
|
|
320
|
+
}
|
|
321
|
+
function generateRegistryJson(ctx) {
|
|
322
|
+
const { baseDir, options, config, exports } = ctx;
|
|
323
|
+
const { exports: _, $schema: __, ...otherConfig } = config;
|
|
324
|
+
const { outputDir, noRootRegistry } = options;
|
|
325
|
+
const registryJson = JSON.stringify({
|
|
326
|
+
$schema: 'https://ui.shadcn.com/schema/registry.json',
|
|
327
|
+
name: '',
|
|
328
|
+
homepage: '',
|
|
329
|
+
items: exports,
|
|
330
|
+
...otherConfig
|
|
331
|
+
}, null, 2);
|
|
332
|
+
if (!noRootRegistry) node_fs.writeFileSync(node_path.join(baseDir, 'registry.json'), registryJson);
|
|
333
|
+
node_fs.writeFileSync(node_path.join(outputDir, 'registry.json'), registryJson);
|
|
334
|
+
}
|
|
335
|
+
function aliasTransformToRelative(ctx) {
|
|
336
|
+
const { exports } = ctx;
|
|
337
|
+
exports.forEach((item)=>{
|
|
338
|
+
const { files } = item;
|
|
339
|
+
files.forEach((file)=>{
|
|
340
|
+
const { aliases } = file;
|
|
341
|
+
if (0 === aliases.length) return;
|
|
342
|
+
aliases.forEach((alias)=>{
|
|
343
|
+
const { relativePath, originalId } = alias;
|
|
344
|
+
file.fileContent = file.fileContent.replace(new RegExp(originalId, 'g'), relativePath);
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
function generateRegistryItemJson(regItem) {
|
|
350
|
+
const { files: _files, extInfo: _, ...otherConfig } = regItem;
|
|
351
|
+
const files = _files.map((item)=>{
|
|
352
|
+
const { path: fromPath, type, target, fileContent } = item;
|
|
353
|
+
return {
|
|
354
|
+
path: fromPath,
|
|
355
|
+
type,
|
|
356
|
+
target,
|
|
357
|
+
content: fileContent
|
|
358
|
+
};
|
|
359
|
+
});
|
|
360
|
+
return {
|
|
361
|
+
$schema: 'https://ui.shadcn.com/schema/registry-item.json',
|
|
362
|
+
...otherConfig,
|
|
363
|
+
files
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
function generateRegistryItemJsons(ctx) {
|
|
367
|
+
const { exports, options } = ctx;
|
|
368
|
+
const { outputDir } = options;
|
|
369
|
+
aliasTransformToRelative(ctx);
|
|
370
|
+
exports.forEach((item)=>{
|
|
371
|
+
const ouputPath = node_path.join(outputDir, `${item.name}.json`);
|
|
372
|
+
const regJson = generateRegistryItemJson(item);
|
|
373
|
+
node_fs.writeFileSync(ouputPath, JSON.stringify(regJson, null, 2));
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
const shadcnRegisterGenerator = createUnplugin((options)=>{
|
|
377
|
+
const ctx = createContext(options);
|
|
378
|
+
return {
|
|
379
|
+
name: 'unplugin-shadcn-register-generator',
|
|
380
|
+
async buildStart () {
|
|
381
|
+
await initContext(ctx);
|
|
382
|
+
generateExports(ctx);
|
|
383
|
+
initOuputDir(ctx);
|
|
384
|
+
generateRegistryItemJsons(ctx);
|
|
385
|
+
clearExportsFields(ctx);
|
|
386
|
+
generateRegistryJson(ctx);
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
});
|
|
390
|
+
const src = shadcnRegisterGenerator;
|
|
391
|
+
export default src;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { TSConfig } from 'pkg-types';
|
|
2
|
+
export interface ShadcnRegisterGeneratorOptions {
|
|
3
|
+
/**
|
|
4
|
+
* registry 仓库地址
|
|
5
|
+
*/
|
|
6
|
+
registryUrl: string;
|
|
7
|
+
/**
|
|
8
|
+
* 输出目录
|
|
9
|
+
* @default './public/r'
|
|
10
|
+
*/
|
|
11
|
+
outputDir?: string;
|
|
12
|
+
/**
|
|
13
|
+
* 基础 target 路径
|
|
14
|
+
* @default '~/'
|
|
15
|
+
*/
|
|
16
|
+
basePath?: string;
|
|
17
|
+
/**
|
|
18
|
+
* 是否不生成根目录的 registry.json
|
|
19
|
+
* @default false
|
|
20
|
+
*/
|
|
21
|
+
noRootRegistry?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface Context {
|
|
24
|
+
options: Required<ShadcnRegisterGeneratorOptions>;
|
|
25
|
+
baseDir: string;
|
|
26
|
+
config: any;
|
|
27
|
+
exports: any;
|
|
28
|
+
tsConfig: TSConfig;
|
|
29
|
+
runCtx: {
|
|
30
|
+
reqistrys: any[];
|
|
31
|
+
dependenciesSet: Set<string>;
|
|
32
|
+
aliasMap: Record<string, Array<{
|
|
33
|
+
originalId: string;
|
|
34
|
+
relativePath: string;
|
|
35
|
+
resolvedId: string;
|
|
36
|
+
}>>;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Context, ShadcnRegisterGeneratorOptions } from '../types';
|
|
2
|
+
export declare function initContext(ctx: Context): Promise<void>;
|
|
3
|
+
export declare function createContext(options: ShadcnRegisterGeneratorOptions): {
|
|
4
|
+
options: Required<ShadcnRegisterGeneratorOptions>;
|
|
5
|
+
baseDir: string;
|
|
6
|
+
config: null;
|
|
7
|
+
exports: null;
|
|
8
|
+
tsConfig: any;
|
|
9
|
+
runCtx: {
|
|
10
|
+
reqistrys: any[];
|
|
11
|
+
dependenciesSet: any;
|
|
12
|
+
aliasMap: {};
|
|
13
|
+
};
|
|
14
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './base';
|
|
2
|
+
export * from './context';
|
|
3
|
+
export * from './generate-exports';
|
|
4
|
+
export * from './generate-shadcn-registry';
|
|
5
|
+
export * from './get-name-from-path';
|
|
6
|
+
export * from './require';
|
|
7
|
+
export * from './ts-paths-resolve';
|
|
8
|
+
export * from './verify';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const fileRequire: NodeJS.Require;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Context } from '../types';
|
|
2
|
+
export declare function tsPathsResolve(id: string, ctx: Context, filePath: string): {
|
|
3
|
+
type: string;
|
|
4
|
+
originalId: string;
|
|
5
|
+
resolvedId: string;
|
|
6
|
+
relativePath?: undefined;
|
|
7
|
+
aliasPath?: undefined;
|
|
8
|
+
} | {
|
|
9
|
+
type: string;
|
|
10
|
+
originalId: string;
|
|
11
|
+
resolvedId: string;
|
|
12
|
+
relativePath: string;
|
|
13
|
+
aliasPath: any;
|
|
14
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Context } from '../types';
|
|
2
|
+
export declare function isNodeOrgModule(id: string): boolean;
|
|
3
|
+
export declare function pathMatch(realPath: string, id: string, isFile: boolean): boolean;
|
|
4
|
+
export declare function fileInWorkspace(filePath: string, ctx: Context): boolean;
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cmtlyt/unplugin-shadcn-registry-generate",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"import": "./dist/index.js"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"configuration-schema.json"
|
|
15
|
+
],
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"pkg-types": "^2.3.0",
|
|
18
|
+
"unplugin": "^2.3.11"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@biomejs/biome": "2.3.8",
|
|
22
|
+
"@commitlint/cli": "^20.3.1",
|
|
23
|
+
"@commitlint/config-conventional": "^20.3.1",
|
|
24
|
+
"@rslib/core": "^0.19.2",
|
|
25
|
+
"@types/node": "^24.10.7",
|
|
26
|
+
"esno": "^4.8.0",
|
|
27
|
+
"husky": "^9.1.7",
|
|
28
|
+
"lint-staged": "^16.2.7",
|
|
29
|
+
"typescript": "^5.9.3"
|
|
30
|
+
},
|
|
31
|
+
"private": false,
|
|
32
|
+
"devEngines": {
|
|
33
|
+
"runtime": {
|
|
34
|
+
"name": "node",
|
|
35
|
+
"version": ">=18.0.0"
|
|
36
|
+
},
|
|
37
|
+
"packageManager": {
|
|
38
|
+
"name": "pnpm"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/cmtlyt/unplugin-shadcn-registry-generate#readme",
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/cmtlyt/unplugin-shadcn-registry-generate/issues"
|
|
44
|
+
},
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "git+https://github.com/cmtlyt/unplugin-shadcn-registry-generate.git"
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"cmtlyt",
|
|
51
|
+
"unplugin",
|
|
52
|
+
"shadcn",
|
|
53
|
+
"shadcn-plugin",
|
|
54
|
+
"generate-registry"
|
|
55
|
+
],
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public",
|
|
58
|
+
"registry": "https://registry.npmjs.org/"
|
|
59
|
+
},
|
|
60
|
+
"scripts": {
|
|
61
|
+
"build": "rslib build",
|
|
62
|
+
"check": "biome check --write",
|
|
63
|
+
"dev": "rslib build --watch",
|
|
64
|
+
"format": "biome format --write"
|
|
65
|
+
}
|
|
66
|
+
}
|