@nocobase/utils 2.1.0-alpha.32 → 2.1.0-alpha.34

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": "@nocobase/utils",
3
- "version": "2.1.0-alpha.32",
3
+ "version": "2.1.0-alpha.34",
4
4
  "main": "lib/index.js",
5
5
  "types": "./lib/index.d.ts",
6
6
  "license": "Apache-2.0",
@@ -21,5 +21,5 @@
21
21
  "object-path": "^0.11.8",
22
22
  "ses": "^1.14.0"
23
23
  },
24
- "gitHead": "1ba7d717e156651db17c615f9b9c48edd669d19b"
24
+ "gitHead": "59970f73bdbea2656e20cd24357bacf78a3f759f"
25
25
  }
@@ -4,3 +4,4 @@ export declare function createStoragePluginSymLink(pluginName: any): Promise<voi
4
4
  export declare function createStoragePluginsSymlink(): Promise<void>;
5
5
  export declare function createDevPluginSymLink(pluginName: any): Promise<void>;
6
6
  export declare function createDevPluginsSymlink(): Promise<void>;
7
+ export declare function syncPluginSymlinks(): Promise<void>;
package/plugin-symlink.js CHANGED
@@ -37,6 +37,36 @@ async function getStoragePluginNames(target) {
37
37
  return plugins;
38
38
  }
39
39
 
40
+ async function getPluginNamesFromSourceRoot(rootPath) {
41
+ if (!(await fs.pathExists(rootPath))) {
42
+ return [];
43
+ }
44
+ return await getStoragePluginNames(rootPath);
45
+ }
46
+
47
+ async function isValidPluginSourcePath(candidate) {
48
+ return await fs.pathExists(resolve(candidate, 'package.json'));
49
+ }
50
+
51
+ function getPluginSourceRoots(storagePluginsPath) {
52
+ return [
53
+ resolve(process.cwd(), 'packages/plugins'),
54
+ resolve(process.cwd(), 'packages/pro-plugins'),
55
+ storagePluginsPath,
56
+ ];
57
+ }
58
+
59
+ async function resolvePluginSourcePath(pluginName, storagePluginsPath) {
60
+ const sourceRoots = getPluginSourceRoots(storagePluginsPath);
61
+ for (const rootPath of sourceRoots) {
62
+ const candidate = resolve(rootPath, pluginName);
63
+ if (await isValidPluginSourcePath(candidate)) {
64
+ return candidate;
65
+ }
66
+ }
67
+ return '';
68
+ }
69
+
40
70
  /**
41
71
  * Ensure the organization directory exists for scoped packages
42
72
  * @param {string} nodeModulesPath - Path to node_modules
@@ -52,22 +82,35 @@ async function ensureOrgDirectory(nodeModulesPath, pluginName) {
52
82
  }
53
83
 
54
84
  /**
55
- * Check if a symlink already points to the correct target
56
- * @param {string} linkPath - Path to the symlink
57
- * @param {string} targetPath - Expected target path
58
- * @returns {Promise<boolean>} True if symlink exists and points to target
85
+ * Check whether node_modules entry is a real directory, symlink, or missing
86
+ * @param {string} linkPath - Path inside node_modules
87
+ * @returns {Promise<'missing'|'dir'|'symlink'|'other'>}
59
88
  */
60
- async function isSymlinkValid(linkPath, targetPath) {
89
+ async function getNodeModulesEntryType(linkPath) {
61
90
  try {
62
- if (await fs.pathExists(linkPath)) {
63
- const realPath = await fs.realpath(linkPath);
64
- return realPath === targetPath;
91
+ const statResult = await fs.lstat(linkPath);
92
+ if (statResult.isSymbolicLink()) {
93
+ return 'symlink';
65
94
  }
95
+ if (statResult.isDirectory()) {
96
+ return 'dir';
97
+ }
98
+ return 'other';
66
99
  } catch (error) {
67
- // If realpath fails, the symlink is invalid
100
+ if (error.code === 'ENOENT') {
101
+ return 'missing';
102
+ }
103
+ return 'other';
104
+ }
105
+ }
106
+
107
+ async function isSymlinkValid(linkPath, targetPath) {
108
+ try {
109
+ const realPath = await fs.realpath(linkPath);
110
+ return realPath === targetPath;
111
+ } catch {
68
112
  return false;
69
113
  }
70
- return false;
71
114
  }
72
115
 
73
116
  /**
@@ -119,13 +162,18 @@ async function createPluginSymLink(pluginName, sourcePath, nodeModulesPath, plug
119
162
  return;
120
163
  }
121
164
 
165
+ const linkPath = resolve(nodeModulesPath, pluginName);
166
+ const entryType = await getNodeModulesEntryType(linkPath);
167
+
168
+ if (entryType === 'dir') {
169
+ return;
170
+ }
171
+
122
172
  // Ensure organization directory exists for scoped packages
123
173
  await ensureOrgDirectory(nodeModulesPath, pluginName);
124
174
 
125
- const linkPath = resolve(nodeModulesPath, pluginName);
126
-
127
175
  // Check if symlink already points to the correct target
128
- if (await isSymlinkValid(linkPath, targetPath)) {
176
+ if (entryType === 'symlink' && (await isSymlinkValid(linkPath, targetPath))) {
129
177
  return; // Symlink is already correct, no need to recreate
130
178
  }
131
179
 
@@ -142,7 +190,6 @@ async function createPluginSymLink(pluginName, sourcePath, nodeModulesPath, plug
142
190
  /**
143
191
  * Create a symlink for a storage plugin
144
192
  * @param {string} pluginName - Name of the plugin
145
- * @returns {Promise<void>}
146
193
  */
147
194
  async function createStoragePluginSymLink(pluginName) {
148
195
  const storagePluginsPath = resolvePluginStoragePath();
@@ -187,8 +234,56 @@ async function createDevPluginsSymlink() {
187
234
  await Promise.all(pluginNames.map((pluginName) => createDevPluginSymLink(pluginName)));
188
235
  }
189
236
 
237
+ /**
238
+ * Create symlinks for all plugins from all sources
239
+ * @returns {Promise<void>}
240
+ */
241
+ async function syncPluginSymlinks() {
242
+ const nodeModulesPath = process.env.NODE_MODULES_PATH;
243
+ if (!nodeModulesPath) {
244
+ return;
245
+ }
246
+
247
+ const storagePluginsPath = resolvePluginStoragePath();
248
+ const sourceRoots = getPluginSourceRoots(storagePluginsPath);
249
+ const pluginNames = new Set();
250
+
251
+ for (const rootPath of sourceRoots) {
252
+ const names = await getPluginNamesFromSourceRoot(rootPath);
253
+ names.forEach((name) => pluginNames.add(name));
254
+ }
255
+
256
+ await Promise.all(
257
+ [...pluginNames].map(async (pluginName) => {
258
+ const linkPath = resolve(nodeModulesPath, pluginName);
259
+ const entryType = await getNodeModulesEntryType(linkPath);
260
+
261
+ if (entryType === 'dir') {
262
+ return;
263
+ }
264
+
265
+ const sourcePath = await resolvePluginSourcePath(pluginName, storagePluginsPath);
266
+ if (!sourcePath) {
267
+ if (entryType === 'symlink' || entryType === 'other') {
268
+ await removeExistingLink(linkPath, pluginName, 'plugin');
269
+ }
270
+ return;
271
+ }
272
+
273
+ if (entryType === 'symlink' && (await isSymlinkValid(linkPath, sourcePath))) {
274
+ return;
275
+ }
276
+
277
+ await ensureOrgDirectory(nodeModulesPath, pluginName);
278
+ await removeExistingLink(linkPath, pluginName, 'plugin');
279
+ await fs.symlink(sourcePath, linkPath, 'dir');
280
+ }),
281
+ );
282
+ }
283
+
190
284
  exports.resolvePluginStoragePath = resolvePluginStoragePath;
191
285
  exports.createStoragePluginSymLink = createStoragePluginSymLink;
192
286
  exports.createStoragePluginsSymlink = createStoragePluginsSymlink;
193
287
  exports.createDevPluginSymLink = createDevPluginSymLink;
194
288
  exports.createDevPluginsSymlink = createDevPluginsSymlink;
289
+ exports.syncPluginSymlinks = syncPluginSymlinks;