@nocobase/utils 1.9.32 → 1.9.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.
Files changed (2) hide show
  1. package/package.json +3 -2
  2. package/plugin-symlink.js +129 -67
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/utils",
3
- "version": "1.9.32",
3
+ "version": "1.9.34",
4
4
  "main": "lib/index.js",
5
5
  "types": "./lib/index.d.ts",
6
6
  "license": "AGPL-3.0",
@@ -11,10 +11,11 @@
11
11
  "dayjs": "^1.11.9",
12
12
  "deepmerge": "^4.2.2",
13
13
  "flat-to-nested": "^1.1.1",
14
+ "fs-extra": "^11.1.1",
14
15
  "graphlib": "^2.1.8",
15
16
  "handlebars": "^4.7.8",
16
17
  "multer": "^1.4.5-lts.2",
17
18
  "object-path": "^0.11.8"
18
19
  },
19
- "gitHead": "40b6cf8117d8ae2435fead40d26d12647706f852"
20
+ "gitHead": "8779a68face5b8f159aef726f73695f319ab7659"
20
21
  }
package/plugin-symlink.js CHANGED
@@ -1,114 +1,176 @@
1
- const { dirname, resolve } = require('path');
2
- const { realpath, readFile, writeFile, readdir, symlink, unlink, mkdir, stat } = require('fs').promises;
1
+ const { resolve } = require('path');
2
+ const fs = require('fs-extra');
3
3
 
4
+ /**
5
+ * Recursively get plugin names from a directory
6
+ * @param {string} target - Target directory to scan
7
+ * @returns {Promise<string[]>} Array of plugin names
8
+ */
4
9
  async function getStoragePluginNames(target) {
5
10
  const plugins = [];
6
- const items = await readdir(target);
11
+ const items = await fs.readdir(target);
12
+
7
13
  for (const item of items) {
14
+ const itemPath = resolve(target, item);
15
+
8
16
  if (item.startsWith('@')) {
9
- const dirPath = resolve(target, item);
10
- const s = await stat(dirPath);
11
- if (!s.isDirectory()) {
17
+ const statResult = await fs.stat(itemPath);
18
+ if (!statResult.isDirectory()) {
12
19
  continue;
13
20
  }
14
- const children = await getStoragePluginNames(dirPath);
15
- plugins.push(
16
- ...children.map((child) => {
17
- return `${item}/${child}`;
18
- }),
19
- );
20
- } else if (await fsExists(resolve(target, item, 'package.json'))) {
21
+ const children = await getStoragePluginNames(itemPath);
22
+ plugins.push(...children.map((child) => `${item}/${child}`));
23
+ } else if (await fs.pathExists(resolve(itemPath, 'package.json'))) {
21
24
  plugins.push(item);
22
25
  }
23
26
  }
27
+
24
28
  return plugins;
25
29
  }
26
30
 
27
- async function fsExists(path) {
31
+ /**
32
+ * Ensure the organization directory exists for scoped packages
33
+ * @param {string} nodeModulesPath - Path to node_modules
34
+ * @param {string} pluginName - Plugin name (may be scoped)
35
+ * @returns {Promise<void>}
36
+ */
37
+ async function ensureOrgDirectory(nodeModulesPath, pluginName) {
38
+ if (pluginName.startsWith('@')) {
39
+ const [orgName] = pluginName.split('/');
40
+ const orgPath = resolve(nodeModulesPath, orgName);
41
+ await fs.ensureDir(orgPath);
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Check if a symlink already points to the correct target
47
+ * @param {string} linkPath - Path to the symlink
48
+ * @param {string} targetPath - Expected target path
49
+ * @returns {Promise<boolean>} True if symlink exists and points to target
50
+ */
51
+ async function isSymlinkValid(linkPath, targetPath) {
28
52
  try {
29
- await stat(path);
30
- return true;
53
+ if (await fs.pathExists(linkPath)) {
54
+ const realPath = await fs.realpath(linkPath);
55
+ return realPath === targetPath;
56
+ }
31
57
  } catch (error) {
58
+ // If realpath fails, the symlink is invalid
32
59
  return false;
33
60
  }
61
+ return false;
34
62
  }
35
63
 
36
- exports.fsExists = fsExists;
37
-
38
- async function createStoragePluginSymLink(pluginName) {
39
- const storagePluginsPath = resolve(process.cwd(), 'storage/plugins');
40
- const nodeModulesPath = process.env.NODE_MODULES_PATH; // resolve(dirname(require.resolve('@nocobase/server/package.json')), 'node_modules');
41
- // const nodeModulesPath = resolve(process.cwd(), 'node_modules');
64
+ /**
65
+ * Remove an existing symlink or file at the given path
66
+ * @param {string} linkPath - Path to remove
67
+ * @param {string} pluginName - Plugin name for error messages
68
+ * @param {string} pluginType - Type of plugin ('storage' or 'dev') for error messages
69
+ * @returns {Promise<void>}
70
+ */
71
+ async function removeExistingLink(linkPath, pluginName, pluginType) {
42
72
  try {
43
- if (pluginName.startsWith('@')) {
44
- const [orgName] = pluginName.split('/');
45
- if (!(await fsExists(resolve(nodeModulesPath, orgName)))) {
46
- await mkdir(resolve(nodeModulesPath, orgName), { recursive: true });
47
- }
73
+ await fs.remove(linkPath);
74
+ } catch (error) {
75
+ // Ignore ENOENT errors (file doesn't exist)
76
+ if (error.code !== 'ENOENT') {
77
+ console.error(`Failed to remove existing symlink for ${pluginType} plugin: ${pluginName}`, error.message);
48
78
  }
49
- const link = resolve(nodeModulesPath, pluginName);
50
- if (await fsExists(link)) {
51
- const realPath = await realpath(link);
52
- if (realPath !== resolve(storagePluginsPath, pluginName)) {
53
- return;
54
- }
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Create a symlink for a plugin
84
+ * @param {string} pluginName - Name of the plugin
85
+ * @param {string} sourcePath - Source directory path
86
+ * @param {string} nodeModulesPath - Target node_modules path
87
+ * @param {string} pluginType - Type of plugin ('storage' or 'dev')
88
+ * @returns {Promise<void>}
89
+ */
90
+ async function createPluginSymLink(pluginName, sourcePath, nodeModulesPath, pluginType) {
91
+ if (!nodeModulesPath) {
92
+ console.error(`NODE_MODULES_PATH is not set, cannot create symlink for ${pluginType} plugin: ${pluginName}`);
93
+ return;
94
+ }
95
+
96
+ try {
97
+ const targetPath = resolve(sourcePath, pluginName);
98
+
99
+ // Check if source exists
100
+ if (!(await fs.pathExists(targetPath))) {
101
+ console.warn(`Source path does not exist for ${pluginType} plugin: ${pluginName} at ${targetPath}`);
102
+ return;
55
103
  }
56
- try {
57
- await unlink(link);
58
- } catch (error) {
59
- console.error(`Failed to remove existing symlink for storage plugin: ${pluginName}`);
104
+
105
+ // Ensure organization directory exists for scoped packages
106
+ await ensureOrgDirectory(nodeModulesPath, pluginName);
107
+
108
+ const linkPath = resolve(nodeModulesPath, pluginName);
109
+
110
+ // Check if symlink already points to the correct target
111
+ if (await isSymlinkValid(linkPath, targetPath)) {
112
+ return; // Symlink is already correct, no need to recreate
60
113
  }
61
- await symlink(resolve(storagePluginsPath, pluginName), link, 'dir');
62
- // console.log(`Created symlink for storage plugin: ${pluginName}`);
114
+
115
+ // Remove existing link if it exists
116
+ await removeExistingLink(linkPath, pluginName, pluginType);
117
+
118
+ // Create new symlink
119
+ await fs.symlink(targetPath, linkPath, 'dir');
63
120
  } catch (error) {
64
- console.error(`Failed to create symlink for storage plugin: ${pluginName}`);
121
+ console.error(`Failed to create symlink for ${pluginType} plugin: ${pluginName}`, error.message);
65
122
  }
66
123
  }
67
124
 
68
- exports.createStoragePluginSymLink = createStoragePluginSymLink;
125
+ /**
126
+ * Create a symlink for a storage plugin
127
+ * @param {string} pluginName - Name of the plugin
128
+ * @returns {Promise<void>}
129
+ */
130
+ async function createStoragePluginSymLink(pluginName) {
131
+ const storagePluginsPath = resolve(process.cwd(), 'storage/plugins');
132
+ const nodeModulesPath = process.env.NODE_MODULES_PATH;
133
+ await createPluginSymLink(pluginName, storagePluginsPath, nodeModulesPath, 'storage');
134
+ }
69
135
 
136
+ /**
137
+ * Create symlinks for all storage plugins
138
+ * @returns {Promise<void>}
139
+ */
70
140
  async function createStoragePluginsSymlink() {
71
141
  const storagePluginsPath = resolve(process.cwd(), 'storage/plugins');
72
- if (!(await fsExists(storagePluginsPath))) {
142
+ if (!(await fs.pathExists(storagePluginsPath))) {
73
143
  return;
74
144
  }
75
145
  const pluginNames = await getStoragePluginNames(storagePluginsPath);
76
146
  await Promise.all(pluginNames.map((pluginName) => createStoragePluginSymLink(pluginName)));
77
147
  }
78
148
 
79
- exports.createStoragePluginsSymlink = createStoragePluginsSymlink;
80
-
149
+ /**
150
+ * Create a symlink for a dev plugin
151
+ * @param {string} pluginName - Name of the plugin
152
+ * @returns {Promise<void>}
153
+ */
81
154
  async function createDevPluginSymLink(pluginName) {
82
155
  const packagePluginsPath = resolve(process.cwd(), 'packages/plugins');
83
- const nodeModulesPath = process.env.NODE_MODULES_PATH; // resolve(dirname(require.resolve('@nocobase/server/package.json')), 'node_modules');
84
- try {
85
- if (pluginName.startsWith('@')) {
86
- const [orgName] = pluginName.split('/');
87
- if (!(await fsExists(resolve(nodeModulesPath, orgName)))) {
88
- await mkdir(resolve(nodeModulesPath, orgName), { recursive: true });
89
- }
90
- }
91
- const link = resolve(nodeModulesPath, pluginName);
92
- try {
93
- await unlink(link);
94
- } catch (error) {
95
- console.error(`Failed to remove existing symlink for dev plugin: ${pluginName}`);
96
- }
97
- await symlink(resolve(packagePluginsPath, pluginName), link, 'dir');
98
- } catch (error) {
99
- console.error(`Failed to create symlink for dev plugin: ${pluginName}`);
100
- }
156
+ const nodeModulesPath = process.env.NODE_MODULES_PATH;
157
+ await createPluginSymLink(pluginName, packagePluginsPath, nodeModulesPath, 'dev');
101
158
  }
102
159
 
103
- exports.createDevPluginSymLink = createDevPluginSymLink;
104
-
160
+ /**
161
+ * Create symlinks for all dev plugins
162
+ * @returns {Promise<void>}
163
+ */
105
164
  async function createDevPluginsSymlink() {
106
- const storagePluginsPath = resolve(process.cwd(), 'packages/plugins');
107
- if (!(await fsExists(storagePluginsPath))) {
165
+ const packagePluginsPath = resolve(process.cwd(), 'packages/plugins');
166
+ if (!(await fs.pathExists(packagePluginsPath))) {
108
167
  return;
109
168
  }
110
- const pluginNames = await getStoragePluginNames(storagePluginsPath);
169
+ const pluginNames = await getStoragePluginNames(packagePluginsPath);
111
170
  await Promise.all(pluginNames.map((pluginName) => createDevPluginSymLink(pluginName)));
112
171
  }
113
172
 
173
+ exports.createStoragePluginSymLink = createStoragePluginSymLink;
174
+ exports.createStoragePluginsSymlink = createStoragePluginsSymlink;
175
+ exports.createDevPluginSymLink = createDevPluginSymLink;
114
176
  exports.createDevPluginsSymlink = createDevPluginsSymlink;