@blocklet/cli 1.16.33 → 1.16.34-beta-20241120-080738-bbbe036c
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 +32 -25
- package/bin/blocklet.js +292 -1
- package/config.example.yml +33 -0
- package/lib/arcblock.js +53 -0
- package/lib/commands/blocklet/add.js +124 -0
- package/lib/commands/blocklet/assets/git-ignore +28 -0
- package/lib/commands/blocklet/assets/index.html +9 -0
- package/lib/commands/blocklet/assets/index.js +14 -0
- package/lib/commands/blocklet/assets/logo.png +0 -0
- package/lib/commands/blocklet/bundle/bundle.js +184 -0
- package/lib/commands/blocklet/bundle/bundlers/blocklet.js +138 -0
- package/lib/commands/blocklet/bundle/bundlers/changelog.js +100 -0
- package/lib/commands/blocklet/bundle/bundlers/logo.js +56 -0
- package/lib/commands/blocklet/bundle/bundlers/markdown.js +241 -0
- package/lib/commands/blocklet/bundle/bundlers/preference.js +50 -0
- package/lib/commands/blocklet/bundle/bundlers/readme.js +43 -0
- package/lib/commands/blocklet/bundle/bundlers/screenshots.js +94 -0
- package/lib/commands/blocklet/bundle/bundlers/simple.js +70 -0
- package/lib/commands/blocklet/bundle/compact/bundle-compact-file.js +48 -0
- package/lib/commands/blocklet/bundle/compact/bundle-merge-extra.js +66 -0
- package/lib/commands/blocklet/bundle/compact/default-external.js +5 -0
- package/lib/commands/blocklet/bundle/compact/index.js +88 -0
- package/lib/commands/blocklet/bundle/index.js +139 -0
- package/lib/commands/blocklet/bundle/pack.js +8 -0
- package/lib/commands/blocklet/bundle/parse-external-dependencies.js +97 -0
- package/lib/commands/blocklet/bundle/simple/index.js +62 -0
- package/lib/commands/blocklet/bundle/zip/archive.js +35 -0
- package/lib/commands/blocklet/bundle/zip/dependencies.js +333 -0
- package/lib/commands/blocklet/bundle/zip/index.js +165 -0
- package/lib/commands/blocklet/bundle/zip/main.js +124 -0
- package/lib/commands/blocklet/bundle/zip/node.js +59 -0
- package/lib/commands/blocklet/bundle/zip/resolve.js +93 -0
- package/lib/commands/blocklet/cleanup.js +52 -0
- package/lib/commands/blocklet/config.js +108 -0
- package/lib/commands/blocklet/connect.js +87 -0
- package/lib/commands/blocklet/create.js +38 -0
- package/lib/commands/blocklet/deploy.js +435 -0
- package/lib/commands/blocklet/dev.js +1000 -0
- package/lib/commands/blocklet/document.js +39 -0
- package/lib/commands/blocklet/exec.js +106 -0
- package/lib/commands/blocklet/init.js +300 -0
- package/lib/commands/blocklet/meta.js +22 -0
- package/lib/commands/blocklet/remove.js +35 -0
- package/lib/commands/blocklet/test.js +201 -0
- package/lib/commands/blocklet/upload.js +105 -0
- package/lib/commands/blocklet/version.js +81 -0
- package/lib/commands/server/cleanup.js +32 -0
- package/lib/commands/server/command.js +131 -0
- package/lib/commands/server/info.js +92 -0
- package/lib/commands/server/init.js +433 -0
- package/lib/commands/server/logs.js +99 -0
- package/lib/commands/server/rescue.js +71 -0
- package/lib/commands/server/start.js +821 -0
- package/lib/commands/server/status.js +107 -0
- package/lib/commands/server/stop.js +163 -0
- package/lib/commands/server/upgrade.js +123 -0
- package/lib/constant.js +21 -2
- package/lib/debug.js +20 -0
- package/lib/manager/config.js +122 -0
- package/lib/manager/deploy.js +75 -0
- package/lib/manager/index.js +23 -0
- package/lib/manager/process.js +47 -0
- package/lib/node.js +214 -0
- package/lib/port.js +19 -0
- package/lib/postinstall.js +3 -0
- package/lib/process/daemon.js +196 -0
- package/lib/process/service.js +86 -0
- package/lib/ui.js +137 -0
- package/lib/util/blocklet/config.js +78 -0
- package/lib/util/blocklet/env.js +172 -0
- package/lib/util/blocklet/meta.js +36 -0
- package/lib/util/blocklet/payment.js +88 -0
- package/lib/util/blocklet/sign.js +21 -0
- package/lib/util/blocklet/tar.js +119 -0
- package/lib/util/convert-to-nosources-sourcemap.js +37 -0
- package/lib/util/docker-status-log.js +17 -0
- package/lib/util/exit-when-server-stopped.js +44 -0
- package/lib/util/get-cli-binary-name.js +8 -0
- package/lib/util/get-download-bundle-step.js +36 -0
- package/lib/util/get-service-instance-number.js +12 -0
- package/lib/util/index.js +626 -0
- package/lib/util/print-error.js +11 -0
- package/lib/util/print.js +9 -0
- package/lib/util/what-uri.js +40 -0
- package/package.json +123 -27
- package/lib/run.d.ts +0 -2
- package/lib/run.js +0 -73
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/* eslint-disable no-use-before-define */
|
|
2
|
+
/* eslint-disable func-names */
|
|
3
|
+
const { dirname, basename, normalize, extname } = require('path');
|
|
4
|
+
|
|
5
|
+
const { glob } = require('glob');
|
|
6
|
+
const { not: notJunk } = require('junk');
|
|
7
|
+
const pkgDir = require('pkg-dir');
|
|
8
|
+
const precinct = require('precinct');
|
|
9
|
+
const requirePackageName = require('require-package-name');
|
|
10
|
+
const formatBackSlash = require('@abtnode/util/lib/format-back-slash');
|
|
11
|
+
|
|
12
|
+
const debug = require('../../../../debug')('bundle:zip');
|
|
13
|
+
const { resolvePathPreserveSymlinks, resolvePackage } = require('./resolve');
|
|
14
|
+
|
|
15
|
+
// Retrieve the paths to the Node.js files to zip.
|
|
16
|
+
// We only include the files actually needed by the function because AWS Lambda
|
|
17
|
+
// has a size limit for the zipped file. It also makes cold starts faster.
|
|
18
|
+
const listNodeFiles = async function ({ srcPath, mainFile, srcDir, stat, extraPaths }) {
|
|
19
|
+
const [treeFiles, depFiles] = await Promise.all([getTreeFiles(srcPath, stat), getDependencies(mainFile, srcDir)]);
|
|
20
|
+
const extraFiles = [];
|
|
21
|
+
for (let i = 0; i < extraPaths.length; i++) {
|
|
22
|
+
const extraPath = extraPaths[i];
|
|
23
|
+
const extension = extname(extraPath);
|
|
24
|
+
if (extension === '.js') {
|
|
25
|
+
// eslint-disable-next-line no-await-in-loop
|
|
26
|
+
const [extraTreeFiles, extraDepFiles] = await Promise.all([
|
|
27
|
+
getTreeFiles(extraPath, stat),
|
|
28
|
+
getDependencies(extraPath, srcDir),
|
|
29
|
+
]);
|
|
30
|
+
extraFiles.push(...extraTreeFiles);
|
|
31
|
+
extraFiles.push(...extraDepFiles);
|
|
32
|
+
} else {
|
|
33
|
+
extraFiles.push(extraPath);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const files = [...treeFiles, ...depFiles, ...extraFiles].map(normalize);
|
|
38
|
+
|
|
39
|
+
const uniqueFiles = [...new Set(files)];
|
|
40
|
+
|
|
41
|
+
// We sort so that the archive's checksum is deterministic.
|
|
42
|
+
const filteredFiles = uniqueFiles.filter(isNotJunk).sort();
|
|
43
|
+
debug('listNodeFiles', {
|
|
44
|
+
srcPath,
|
|
45
|
+
mainFile,
|
|
46
|
+
extraPaths,
|
|
47
|
+
uniqueFiles: uniqueFiles.length,
|
|
48
|
+
filteredFiles: filteredFiles.length,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return filteredFiles;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// When using a directory, we include all its descendants except `node_modules`
|
|
55
|
+
const getTreeFiles = function (srcPath, stat) {
|
|
56
|
+
if (!stat.isDirectory()) {
|
|
57
|
+
return [srcPath];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const tmpPath = formatBackSlash(srcPath);
|
|
61
|
+
return glob(`${tmpPath}/**`, {
|
|
62
|
+
ignore: `${tmpPath}/**/node_modules/**`,
|
|
63
|
+
nodir: true,
|
|
64
|
+
absolute: true,
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Remove temporary files like *~, *.swp, etc.
|
|
69
|
+
const isNotJunk = function (file) {
|
|
70
|
+
if (file.includes('.pnpm')) {
|
|
71
|
+
console.warn('Maybe a dependency is missing in your package.json', file);
|
|
72
|
+
}
|
|
73
|
+
return file.includes('.pnpm') === false && notJunk(basename(file));
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Retrieve all the files recursively required by a Node.js file
|
|
77
|
+
const getDependencies = async function (mainFile, srcDir) {
|
|
78
|
+
const packageRoot = await pkgDir(srcDir);
|
|
79
|
+
const packageJson = getPackageJson(packageRoot);
|
|
80
|
+
|
|
81
|
+
const state = { localFiles: [], modulePaths: [] };
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
return await getFileDependencies(mainFile, packageJson, state);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
error.message = `In file "${mainFile}": ${error.message}`;
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const getPackageJson = function (packageRoot) {
|
|
92
|
+
if (packageRoot === undefined) {
|
|
93
|
+
return {};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const packageJsonPath = `${packageRoot}/package.json`;
|
|
97
|
+
try {
|
|
98
|
+
// eslint-disable-next-line
|
|
99
|
+
return require(packageJsonPath);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
throw new Error(`${packageJsonPath} is invalid JSON: ${error.message}`);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const getFileDependencies = async function (path, packageJson, state) {
|
|
106
|
+
if (state.localFiles.includes(path)) {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
state.localFiles.push(path);
|
|
111
|
+
|
|
112
|
+
const basedir = dirname(path);
|
|
113
|
+
// This parses JavaScript in `path` to retrieve all the `require()` statements
|
|
114
|
+
// TODO: `precinct.paperwork()` uses `fs.readFileSync()` under the hood,
|
|
115
|
+
// but should use `fs.readFile()` instead
|
|
116
|
+
const dependencies = precinct.paperwork(path, { includeCore: false });
|
|
117
|
+
|
|
118
|
+
const depsPaths = await Promise.all(
|
|
119
|
+
dependencies.map((dependency) => getImportDependencies(dependency, basedir, packageJson, state))
|
|
120
|
+
);
|
|
121
|
+
// debug('getFileDependencies', { path, depsPaths });
|
|
122
|
+
return [].concat(...depsPaths);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// `require()` statements can be either `require('moduleName')` or
|
|
126
|
+
// `require(path)`
|
|
127
|
+
const getImportDependencies = function (dependency, basedir, packageJson, state) {
|
|
128
|
+
if (LOCAL_IMPORT_REGEXP.test(dependency)) {
|
|
129
|
+
return getLocalImportDependencies(dependency, basedir, packageJson, state);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return getModuleDependencies(dependency, basedir, state, packageJson);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const LOCAL_IMPORT_REGEXP = /^(\.|\/)/;
|
|
136
|
+
|
|
137
|
+
// When a file requires another one, we apply the top-level logic recursively
|
|
138
|
+
const getLocalImportDependencies = async function (dependency, basedir, packageJson, state) {
|
|
139
|
+
const dependencyPath = await resolvePathPreserveSymlinks(dependency, basedir);
|
|
140
|
+
const depsPath = await getFileDependencies(dependencyPath, packageJson, state);
|
|
141
|
+
return [dependencyPath, ...depsPath];
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// When a file requires a module, we find its path inside `node_modules` and
|
|
145
|
+
// use all its published files. We also recurse on the module's dependencies.
|
|
146
|
+
const getModuleDependencies = async function (dependency, basedir, state, packageJson) {
|
|
147
|
+
const moduleName = getModuleName(dependency);
|
|
148
|
+
|
|
149
|
+
// Happens when doing require("@scope") (not "@scope/name") or other oddities
|
|
150
|
+
// Ignore those.
|
|
151
|
+
if (moduleName === null) {
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
return await getModuleNameDependencies(moduleName, basedir, state);
|
|
157
|
+
} catch (error) {
|
|
158
|
+
return handleModuleNotFound({ error, moduleName, packageJson });
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// When doing require("moduleName/file/path"), only keep `moduleName`
|
|
163
|
+
const getModuleName = function (dependency) {
|
|
164
|
+
const dependencyA = dependency.replace(BACKSLASH_REGEXP, '/');
|
|
165
|
+
const moduleName = requirePackageName(dependencyA);
|
|
166
|
+
return moduleName;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// Windows path normalization
|
|
170
|
+
const BACKSLASH_REGEXP = /\\/g;
|
|
171
|
+
|
|
172
|
+
const getModuleNameDependencies = async function (moduleName, basedir, state) {
|
|
173
|
+
if (isExcludedModule(moduleName)) {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Find the Node.js module directory path
|
|
178
|
+
const packagePath = await resolvePackage(moduleName, basedir);
|
|
179
|
+
|
|
180
|
+
if (packagePath === undefined) {
|
|
181
|
+
return [];
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const modulePath = dirname(packagePath);
|
|
185
|
+
|
|
186
|
+
if (state.modulePaths.includes(modulePath)) {
|
|
187
|
+
return [];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
state.modulePaths.push(modulePath);
|
|
191
|
+
|
|
192
|
+
// eslint-disable-next-line
|
|
193
|
+
const pkg = require(packagePath);
|
|
194
|
+
|
|
195
|
+
const [publishedFiles, sideFiles, depsPaths] = await Promise.all([
|
|
196
|
+
getPublishedFiles(modulePath),
|
|
197
|
+
getSideFiles(modulePath, moduleName),
|
|
198
|
+
getNestedModules(modulePath, state, pkg),
|
|
199
|
+
]);
|
|
200
|
+
// debug('getModuleNameDependencies', { moduleName, basedir, packagePath, modulePath, publishedFiles, depsPaths });
|
|
201
|
+
return [...publishedFiles, ...sideFiles, ...depsPaths];
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const EXCLUDED_MODULES = [];
|
|
205
|
+
const isExcludedModule = function (moduleName) {
|
|
206
|
+
return (
|
|
207
|
+
EXCLUDED_MODULES.includes(moduleName) || moduleName.startsWith('@types/') || moduleName.startsWith('@cypress/')
|
|
208
|
+
);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// Some modules generate source files on `postinstall` that are not located
|
|
212
|
+
// inside the module's directory itself.
|
|
213
|
+
const getSideFiles = function (modulePath, moduleName) {
|
|
214
|
+
const sideFiles = SIDE_FILES[moduleName];
|
|
215
|
+
if (sideFiles === undefined) {
|
|
216
|
+
return [];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return getPublishedFiles(`${modulePath}/${sideFiles}`);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const SIDE_FILES = {
|
|
223
|
+
'@prisma/client': '../../.prisma',
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
// We use all the files published by the Node.js except some that are not needed
|
|
227
|
+
const getPublishedFiles = async function (modulePath) {
|
|
228
|
+
const ignore = getIgnoredFiles(modulePath);
|
|
229
|
+
const publishedFiles = await glob(formatBackSlash(`${modulePath}/**`), {
|
|
230
|
+
ignore,
|
|
231
|
+
nodir: true,
|
|
232
|
+
absolute: true,
|
|
233
|
+
dot: false,
|
|
234
|
+
});
|
|
235
|
+
return publishedFiles;
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const getIgnoredFiles = function (modulePath) {
|
|
239
|
+
return IGNORED_FILES.map((ignoreFile) => `${modulePath}/${ignoreFile}`);
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// To make the zip archive smaller, we remove those.
|
|
243
|
+
const IGNORED_FILES = [
|
|
244
|
+
'node_modules/**',
|
|
245
|
+
'.blocklet/**',
|
|
246
|
+
'.abtnode/**',
|
|
247
|
+
'coverage/**',
|
|
248
|
+
'tests/**',
|
|
249
|
+
'test/**',
|
|
250
|
+
'doc/**',
|
|
251
|
+
'docs/**',
|
|
252
|
+
'.npmignore',
|
|
253
|
+
'package-lock.json',
|
|
254
|
+
'yarn.lock',
|
|
255
|
+
'**/*.js.map',
|
|
256
|
+
'**/*.css.map',
|
|
257
|
+
'**/*.d.ts',
|
|
258
|
+
'**/*.d.ts.map',
|
|
259
|
+
'*.log',
|
|
260
|
+
'*.lock',
|
|
261
|
+
'*~',
|
|
262
|
+
'*.ts',
|
|
263
|
+
'*.patch',
|
|
264
|
+
];
|
|
265
|
+
|
|
266
|
+
// Apply the Node.js module logic recursively on its own dependencies, using
|
|
267
|
+
// the `package.json` `dependencies`, `peerDependencies` and
|
|
268
|
+
// `optionalDependencies` keys
|
|
269
|
+
const getNestedModules = async function (modulePath, state, pkg) {
|
|
270
|
+
const dependencies = getNestedDependencies(pkg);
|
|
271
|
+
const depsPaths = await Promise.all(
|
|
272
|
+
dependencies.map((dependency) => getModuleDependencies(dependency, modulePath, state, pkg))
|
|
273
|
+
);
|
|
274
|
+
return [].concat(...depsPaths);
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const getNestedDependencies = function ({ dependencies = {}, peerDependencies = {}, optionalDependencies = {} }) {
|
|
278
|
+
return [
|
|
279
|
+
...Object.keys(dependencies),
|
|
280
|
+
...Object.keys(peerDependencies).filter(shouldIncludePeerDependency),
|
|
281
|
+
...Object.keys(optionalDependencies),
|
|
282
|
+
];
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const shouldIncludePeerDependency = function (name) {
|
|
286
|
+
return !EXCLUDED_PEER_DEPENDENCIES.includes(name);
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const EXCLUDED_PEER_DEPENDENCIES = ['@prisma/cli', 'prisma2'];
|
|
290
|
+
|
|
291
|
+
// Modules can be required conditionally (inside an `if` or `try`/`catch` block).
|
|
292
|
+
// When a `require()` statement is found but the module is not found, it is
|
|
293
|
+
// possible that that block either always evaluates to:
|
|
294
|
+
// - `false`: in which case, we should not bundle the dependency
|
|
295
|
+
// - `true`: in which case, we should report the dependency as missing
|
|
296
|
+
// Those conditional modules might be:
|
|
297
|
+
// - present in the `package.json` `dependencies`
|
|
298
|
+
// - present in the `package.json` `optionalDependencies`
|
|
299
|
+
// - present in the `package.json` `peerDependencies`
|
|
300
|
+
// - not present in the `package.json`, if the module author wants its users
|
|
301
|
+
// to explicitly install it as an optional dependency.
|
|
302
|
+
// The current implementation:
|
|
303
|
+
// - when parsing `require()` statements inside function files, always consider
|
|
304
|
+
// conditional modules to be included, i.e. report them if not found.
|
|
305
|
+
// This is because our current parsing logic does not know whether a
|
|
306
|
+
// `require()` is conditional or not.
|
|
307
|
+
// - when parsing module dependencies, ignore `require()` statements if not
|
|
308
|
+
// present in the `package.json` `*dependencies`. I.e. user must manually
|
|
309
|
+
// install them if the module is used.
|
|
310
|
+
// `optionalDependencies`:
|
|
311
|
+
// - are not reported when missing
|
|
312
|
+
// - are included in module dependencies
|
|
313
|
+
const handleModuleNotFound = function ({ error, moduleName, packageJson }) {
|
|
314
|
+
if (error.code === 'MODULE_NOT_FOUND' && isOptionalModule(moduleName, packageJson)) {
|
|
315
|
+
return [];
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
throw error;
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const isOptionalModule = function (
|
|
322
|
+
moduleName,
|
|
323
|
+
{ optionalDependencies = {}, peerDependenciesMeta = {}, peerDependencies = {} }
|
|
324
|
+
) {
|
|
325
|
+
return (
|
|
326
|
+
optionalDependencies[moduleName] !== undefined ||
|
|
327
|
+
(peerDependenciesMeta[moduleName] &&
|
|
328
|
+
peerDependenciesMeta[moduleName].optional &&
|
|
329
|
+
peerDependencies[moduleName] !== undefined)
|
|
330
|
+
);
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
module.exports = { listNodeFiles };
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const { join, dirname, basename, extname, relative } = require('path');
|
|
5
|
+
const { BLOCKLET_BUNDLE_FOLDER, BLOCKLET_BUNDLE_FILE, BLOCKLET_ENTRY_FILE } = require('@blocklet/constant');
|
|
6
|
+
const parseBlockletMeta = require('@blocklet/meta/lib/parse');
|
|
7
|
+
const detectWorkspace = require('@abtnode/util/lib/detect-workspace');
|
|
8
|
+
|
|
9
|
+
const { print, printInfo, printSuccess, printError } = require('../../../../util');
|
|
10
|
+
const { wrapSpinner } = require('../../../../ui');
|
|
11
|
+
const main = require('./main');
|
|
12
|
+
const pack = require('../pack');
|
|
13
|
+
const { createBlockletBundle, createBlockletEntry, getExtraFiles } = require('../bundle');
|
|
14
|
+
const { logTar, getContents } = require('../../../../util/blocklet/tar');
|
|
15
|
+
|
|
16
|
+
const debug = require('../../../../debug')('bundle:zip');
|
|
17
|
+
|
|
18
|
+
module.exports.run = async ({
|
|
19
|
+
meta,
|
|
20
|
+
blockletDir,
|
|
21
|
+
createRelease = false,
|
|
22
|
+
inMonoRepo = false,
|
|
23
|
+
withChangeLog = true,
|
|
24
|
+
externals = [],
|
|
25
|
+
externalManager = 'npm',
|
|
26
|
+
}) => {
|
|
27
|
+
// eslint-disable-next-line no-param-reassign
|
|
28
|
+
meta = meta || parseBlockletMeta(blockletDir, { ensureFiles: true });
|
|
29
|
+
|
|
30
|
+
const destFolder = join(blockletDir, BLOCKLET_BUNDLE_FOLDER);
|
|
31
|
+
|
|
32
|
+
if (fs.existsSync(destFolder)) {
|
|
33
|
+
fs.removeSync(destFolder);
|
|
34
|
+
} else {
|
|
35
|
+
fs.mkdirSync(destFolder, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const workspace = detectWorkspace(blockletDir);
|
|
39
|
+
const extraFiles = getExtraFiles(blockletDir, meta);
|
|
40
|
+
|
|
41
|
+
const zipConfig = {
|
|
42
|
+
options: {},
|
|
43
|
+
destFolder,
|
|
44
|
+
extraPaths: [],
|
|
45
|
+
skippable: true,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const mainPath = join(blockletDir, meta.main || '/');
|
|
49
|
+
const hasMainFile = fs.statSync(mainPath).isFile();
|
|
50
|
+
|
|
51
|
+
// 1. for dapp entry
|
|
52
|
+
if (hasMainFile) {
|
|
53
|
+
zipConfig.srcFolder = dirname(mainPath);
|
|
54
|
+
zipConfig.srcFile = basename(mainPath);
|
|
55
|
+
zipConfig.srcFilter = (x) => [zipConfig.srcFile].includes(x);
|
|
56
|
+
zipConfig.skippable = false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 2. for hook scripts
|
|
60
|
+
if (extraFiles.length) {
|
|
61
|
+
zipConfig.extraPaths = extraFiles.map((x) => join(blockletDir, x));
|
|
62
|
+
zipConfig.skippable = false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 3. static blocklets may also include hook scripts
|
|
66
|
+
if (!zipConfig.srcFile && extraFiles.length) {
|
|
67
|
+
const srcFile = zipConfig.extraPaths.shift();
|
|
68
|
+
zipConfig.srcFolder = dirname(srcFile);
|
|
69
|
+
zipConfig.srcFile = basename(srcFile);
|
|
70
|
+
zipConfig.srcFilter = (x) => [zipConfig.srcFile].includes(x);
|
|
71
|
+
zipConfig.skippable = false;
|
|
72
|
+
}
|
|
73
|
+
debug('config', zipConfig);
|
|
74
|
+
debug('workspace', workspace);
|
|
75
|
+
|
|
76
|
+
// 4. create zip bundles
|
|
77
|
+
let archive;
|
|
78
|
+
if (zipConfig.skippable === false) {
|
|
79
|
+
const archives = await wrapSpinner(`Bundle blocklet from entry: ${chalk.cyan(meta.main)}`, () =>
|
|
80
|
+
main.zipEntries(zipConfig)
|
|
81
|
+
);
|
|
82
|
+
debug('archives', archives);
|
|
83
|
+
|
|
84
|
+
[archive] = archives;
|
|
85
|
+
if (!archive) {
|
|
86
|
+
printError('Failed to create zipped bundle');
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 5. handle extraFiles
|
|
92
|
+
const entryParts = ['.'];
|
|
93
|
+
let relativePath = '';
|
|
94
|
+
|
|
95
|
+
if (inMonoRepo && workspace) {
|
|
96
|
+
relativePath = relative(workspace.dir, blockletDir);
|
|
97
|
+
} else if (archive) {
|
|
98
|
+
relativePath = relative(archive.prefix, blockletDir);
|
|
99
|
+
}
|
|
100
|
+
if (relativePath) {
|
|
101
|
+
entryParts.push(relativePath);
|
|
102
|
+
extraFiles.forEach((f) => {
|
|
103
|
+
if (extname(f) === '.js') {
|
|
104
|
+
const absoluteFilepath = join(blockletDir, BLOCKLET_BUNDLE_FOLDER, f);
|
|
105
|
+
fs.ensureDirSync(dirname(absoluteFilepath));
|
|
106
|
+
|
|
107
|
+
const requirePath = relative(
|
|
108
|
+
dirname(absoluteFilepath),
|
|
109
|
+
join(blockletDir, BLOCKLET_BUNDLE_FOLDER, relativePath, f)
|
|
110
|
+
);
|
|
111
|
+
fs.writeFileSync(absoluteFilepath, createBlockletEntry(blockletDir, requirePath));
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 6. produce a `blocklet.js` file
|
|
117
|
+
let outputFile;
|
|
118
|
+
if (hasMainFile) {
|
|
119
|
+
entryParts.push(meta.main);
|
|
120
|
+
outputFile = join(blockletDir, BLOCKLET_BUNDLE_FOLDER, BLOCKLET_ENTRY_FILE);
|
|
121
|
+
fs.writeFileSync(outputFile, createBlockletEntry(blockletDir, entryParts.filter(Boolean).join('/')));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 7. rename bundle to blocklet.zip
|
|
125
|
+
let outputZip;
|
|
126
|
+
if (archive) {
|
|
127
|
+
outputZip = join(blockletDir, BLOCKLET_BUNDLE_FOLDER, BLOCKLET_BUNDLE_FILE);
|
|
128
|
+
fs.renameSync(archive.path, outputZip);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
print('');
|
|
132
|
+
printInfo(`Source: ${mainPath}`);
|
|
133
|
+
if (outputFile) {
|
|
134
|
+
printInfo(`Entry: ${outputFile}`);
|
|
135
|
+
}
|
|
136
|
+
if (outputZip) {
|
|
137
|
+
printInfo(`Archive: ${outputZip}`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 8. create bundle
|
|
141
|
+
await wrapSpinner(`Creating blocklet bundle in ${chalk.cyan(BLOCKLET_BUNDLE_FOLDER)}...`, async () => {
|
|
142
|
+
await createBlockletBundle({
|
|
143
|
+
blockletDir,
|
|
144
|
+
meta,
|
|
145
|
+
updates: hasMainFile ? { main: basename(outputZip) } : {},
|
|
146
|
+
inMonoRepo,
|
|
147
|
+
withChangeLog,
|
|
148
|
+
externals,
|
|
149
|
+
externalManager,
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
printSuccess(`Blocklet ${chalk.cyan(`${meta.name}@${meta.version}`)} is successfully bundled!`);
|
|
154
|
+
|
|
155
|
+
// 9. create release
|
|
156
|
+
if (createRelease) {
|
|
157
|
+
print('');
|
|
158
|
+
// eslint-disable-next-line no-shadow
|
|
159
|
+
const { tarball, meta } = await pack(blockletDir);
|
|
160
|
+
const pkgContents = await getContents(meta, tarball);
|
|
161
|
+
logTar(pkgContents);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
process.exit(0);
|
|
165
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/* eslint-disable no-use-before-define */
|
|
2
|
+
/* eslint-disable func-names */
|
|
3
|
+
const { readdir, lstat } = require('fs');
|
|
4
|
+
const { join, resolve, dirname, basename, extname } = require('path');
|
|
5
|
+
|
|
6
|
+
const cpFile = require('cp-file');
|
|
7
|
+
const locatePath = require('locate-path');
|
|
8
|
+
const makeDir = require('make-dir');
|
|
9
|
+
const pMap = require('p-map');
|
|
10
|
+
const { promisify } = require('util');
|
|
11
|
+
|
|
12
|
+
const { listNodeFiles } = require('./dependencies');
|
|
13
|
+
const { zipNodeJs } = require('./node');
|
|
14
|
+
|
|
15
|
+
const debug = require('../../../../debug')('bundle:zip');
|
|
16
|
+
|
|
17
|
+
const pReaddir = promisify(readdir);
|
|
18
|
+
const pLstat = promisify(lstat);
|
|
19
|
+
|
|
20
|
+
// reduce `srcFolder/*` (Node.js files) to `destFolder/*.zip` so it can be used by Blocklet Server
|
|
21
|
+
const zipEntries = async function ({ srcFolder, destFolder, srcFilter, extraPaths, options = {} }) {
|
|
22
|
+
const { parallelLimit = 5 } = options;
|
|
23
|
+
const srcPaths = await getSrcPaths(srcFolder, srcFilter);
|
|
24
|
+
|
|
25
|
+
const zipped = await pMap(srcPaths, (srcPath) => zipEntry({ srcPath, destFolder, extraPaths }), {
|
|
26
|
+
concurrency: parallelLimit,
|
|
27
|
+
});
|
|
28
|
+
return zipped.filter(Boolean);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const zipEntry = async function ({ srcPath, destFolder, extraPaths }) {
|
|
32
|
+
const { runtime, filename, extension, srcDir, stat, mainFile } = await getEntryInfo(srcPath);
|
|
33
|
+
|
|
34
|
+
if (runtime === undefined) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const srcFiles = await getSrcFiles({ runtime, filename, stat, mainFile, extension, srcPath, srcDir, extraPaths });
|
|
39
|
+
|
|
40
|
+
await makeDir(destFolder);
|
|
41
|
+
|
|
42
|
+
if (runtime === 'js') {
|
|
43
|
+
if (extension === '.zip') {
|
|
44
|
+
const destPath = join(destFolder, filename);
|
|
45
|
+
await cpFile(srcPath, destPath);
|
|
46
|
+
// eslint-disable-next-line consistent-return
|
|
47
|
+
return { path: destPath, runtime };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const destPath = join(destFolder, `${basename(filename, '.js')}.zip`);
|
|
51
|
+
const prefix = await zipNodeJs(srcFiles, destPath, filename, mainFile);
|
|
52
|
+
// eslint-disable-next-line consistent-return
|
|
53
|
+
return { path: destPath, prefix, runtime };
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const getSrcPaths = async function (srcFolder, srcFilter) {
|
|
58
|
+
if (!srcFolder) {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
const filenames = await listFilenames(srcFolder);
|
|
62
|
+
const srcPaths = filenames.filter(srcFilter).map((x) => resolve(srcFolder, x));
|
|
63
|
+
debug('getSrcPaths', { srcFolder, srcPaths });
|
|
64
|
+
return srcPaths;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const listFilenames = async function (srcFolder) {
|
|
68
|
+
try {
|
|
69
|
+
return await pReaddir(srcFolder);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
throw new Error(`entry folder does not exist: ${srcFolder}`);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const getEntryInfo = async function (srcPath) {
|
|
76
|
+
const { filename, stat, mainFile, extension, srcDir } = await getSrcInfo(srcPath);
|
|
77
|
+
|
|
78
|
+
if (mainFile === undefined) {
|
|
79
|
+
return {};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (extension === '.zip' || extension === '.js') {
|
|
83
|
+
return { runtime: 'js', filename, stat, mainFile, extension, srcPath, srcDir };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {};
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const getSrcInfo = async function (srcPath) {
|
|
90
|
+
const filename = basename(srcPath);
|
|
91
|
+
if (filename === 'node_modules') {
|
|
92
|
+
return {};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const stat = await pLstat(srcPath);
|
|
96
|
+
const mainFile = await getMainFile(srcPath, filename, stat);
|
|
97
|
+
if (mainFile === undefined) {
|
|
98
|
+
return {};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const extension = extname(mainFile);
|
|
102
|
+
const srcDir = stat.isDirectory() ? srcPath : dirname(srcPath);
|
|
103
|
+
return { filename, stat, mainFile, extension, srcDir };
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Each `srcPath` can also be a directory with an `index.js` file or a file
|
|
107
|
+
// using the same filename as its directory
|
|
108
|
+
const getMainFile = function (srcPath, filename, stat) {
|
|
109
|
+
if (!stat.isDirectory()) {
|
|
110
|
+
return srcPath;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return locatePath([join(srcPath, `${filename}.js`), join(srcPath, 'index.js')], { type: 'file' });
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const getSrcFiles = function ({ runtime, filename, stat, mainFile, extension, srcPath, srcDir, extraPaths }) {
|
|
117
|
+
if (runtime === 'js' && extension === '.js') {
|
|
118
|
+
return listNodeFiles({ srcPath, filename, mainFile, srcDir, stat, extraPaths });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return [srcPath];
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
module.exports = { zipEntries, zipEntry };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/* eslint-disable no-use-before-define */
|
|
2
|
+
/* eslint-disable func-names */
|
|
3
|
+
const { stat } = require('fs');
|
|
4
|
+
const { dirname, normalize, sep } = require('path');
|
|
5
|
+
|
|
6
|
+
const commonPathPrefix = require('common-path-prefix');
|
|
7
|
+
const unixify = require('unixify');
|
|
8
|
+
const { promisify } = require('util');
|
|
9
|
+
|
|
10
|
+
const { startZip, addZipFile, endZip } = require('./archive');
|
|
11
|
+
|
|
12
|
+
const pStat = promisify(stat);
|
|
13
|
+
|
|
14
|
+
// zip a Node.js function file
|
|
15
|
+
const zipNodeJs = async function (srcFiles, destPath) {
|
|
16
|
+
const { archive, output } = startZip(destPath);
|
|
17
|
+
|
|
18
|
+
const dirnames = srcFiles.map(dirname);
|
|
19
|
+
const commonPrefix = commonPathPrefix(dirnames);
|
|
20
|
+
|
|
21
|
+
const srcFilesInfos = await Promise.all(srcFiles.map(addStat));
|
|
22
|
+
|
|
23
|
+
// We ensure this is not async, so that the archive's checksum is
|
|
24
|
+
// deterministic. Otherwise it depends on the order the files were added.
|
|
25
|
+
// eslint-disable-next-line no-shadow
|
|
26
|
+
srcFilesInfos.forEach(({ srcFile, stat }) => {
|
|
27
|
+
zipJsFile({ srcFile, commonPrefix, archive, stat });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
await endZip(archive, output);
|
|
31
|
+
|
|
32
|
+
return commonPrefix;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const addStat = async function (srcFile) {
|
|
36
|
+
// eslint-disable-next-line no-shadow
|
|
37
|
+
const stat = await pStat(srcFile);
|
|
38
|
+
return { srcFile, stat };
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// eslint-disable-next-line no-shadow
|
|
42
|
+
const zipJsFile = function ({ srcFile, commonPrefix, archive, stat }) {
|
|
43
|
+
const filename = normalizeFilePath(srcFile, commonPrefix);
|
|
44
|
+
addZipFile(archive, srcFile, filename, stat);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// `adm-zip` and `require()` expect Unix paths.
|
|
48
|
+
// We remove the common path prefix.
|
|
49
|
+
// With files on different Windows drives, we remove the drive letter.
|
|
50
|
+
const normalizeFilePath = function (path, commonPrefix) {
|
|
51
|
+
const pathA = normalize(path);
|
|
52
|
+
const pathB = pathA.replace(commonPrefix, `${ZIP_ROOT_DIR}${sep}`);
|
|
53
|
+
const pathC = unixify(pathB);
|
|
54
|
+
return pathC;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const ZIP_ROOT_DIR = '.';
|
|
58
|
+
|
|
59
|
+
module.exports = { zipNodeJs, ZIP_ROOT_DIR };
|