sandstone-cli 0.5.4 → 0.6.1
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 +31 -30
- package/bin/run +0 -0
- package/lib/{buildProject.d.ts → build/buildProject.d.ts} +7 -4
- package/lib/build/buildProject.js +461 -0
- package/lib/build/graph.d.ts +49 -0
- package/lib/build/graph.js +197 -0
- package/lib/commands/build.d.ts +2 -2
- package/lib/commands/build.js +4 -4
- package/lib/commands/create.d.ts +3 -2
- package/lib/commands/create.js +51 -42
- package/lib/commands/update.js +18 -18
- package/lib/commands/watch.d.ts +2 -2
- package/lib/commands/watch.js +26 -14
- package/lib/template/README.md +2 -2
- package/lib/utils.d.ts +1 -0
- package/lib/utils.js +5 -4
- package/oclif.manifest.json +1 -1
- package/package.json +20 -4
- package/lib/buildProject.js +0 -257
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ $ npm install -g sandstone-cli
|
|
|
19
19
|
$ sand COMMAND
|
|
20
20
|
running command...
|
|
21
21
|
$ sand (-v|--version|version)
|
|
22
|
-
sandstone-cli/0.
|
|
22
|
+
sandstone-cli/0.6.1 linux-x64 node-v16.19.0
|
|
23
23
|
$ sand --help [COMMAND]
|
|
24
24
|
USAGE
|
|
25
25
|
$ sand COMMAND
|
|
@@ -36,7 +36,7 @@ USAGE
|
|
|
36
36
|
|
|
37
37
|
## `sand build PATH CONFIG-PATH`
|
|
38
38
|
|
|
39
|
-
Build the
|
|
39
|
+
Build the packs. ⛏
|
|
40
40
|
|
|
41
41
|
```
|
|
42
42
|
USAGE
|
|
@@ -47,7 +47,7 @@ ARGUMENTS
|
|
|
47
47
|
CONFIG-PATH [default: .] Path of the sandstone.config.ts folder.
|
|
48
48
|
|
|
49
49
|
OPTIONS
|
|
50
|
-
-d, --dry Do not save the
|
|
50
|
+
-d, --dry Do not save the pack. Mostly useful with `verbose`.
|
|
51
51
|
-h, --help show CLI help
|
|
52
52
|
-p, --production Runs Sandstone in production mode. This sets process.env.SANDSTONE_ENV to "production".
|
|
53
53
|
-v, --verbose Log all resulting resources: functions, advancements...
|
|
@@ -55,23 +55,23 @@ OPTIONS
|
|
|
55
55
|
--autoReload=port Automatically reload your data pack in-game. Requires to open the world to LAN with
|
|
56
56
|
cheats enabled, and to specify the port.
|
|
57
57
|
|
|
58
|
+
--clientPath=clientPath Path of the client folder. Override the value specified in the configuration file.
|
|
59
|
+
|
|
58
60
|
--description=description Description of the data pack. Override the value specified in the configuration file.
|
|
59
61
|
|
|
60
62
|
--formatVersion=formatVersion Pack format version. Override the value specified in the configuration file.
|
|
61
63
|
|
|
62
64
|
--fullTrace Show the full stack trace on errors.
|
|
63
65
|
|
|
64
|
-
--minecraftPath=minecraftPath Path of the .minecraft folder. Override the value specified in the configuration file.
|
|
65
|
-
|
|
66
66
|
--name=name Name of the data pack. Override the value specified in the configuration file.
|
|
67
67
|
|
|
68
68
|
--namespace=namespace The default namespace. Override the value specified in the configuration file.
|
|
69
69
|
|
|
70
|
-
--
|
|
70
|
+
--root Save the data pack & resource pack in the .minecraft/datapacks &
|
|
71
|
+
.minecraft/resource_packs folders. Override the value specified in the configuration
|
|
71
72
|
file.
|
|
72
73
|
|
|
73
|
-
--
|
|
74
|
-
in the configuration file.
|
|
74
|
+
--serverPath=serverPath Path of the server folder. Override the value specified in the configuration file.
|
|
75
75
|
|
|
76
76
|
--strictErrors Stop data pack compilation on type errors.
|
|
77
77
|
|
|
@@ -84,7 +84,7 @@ EXAMPLES
|
|
|
84
84
|
$ sand build --verbose --dry
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
_See code: [src/commands/build.ts](https://github.com/TheMrZZ/sandstone-cli/blob/v0.
|
|
87
|
+
_See code: [src/commands/build.ts](https://github.com/TheMrZZ/sandstone-cli/blob/v0.6.1/src/commands/build.ts)_
|
|
88
88
|
|
|
89
89
|
## `sand create PROJECT-NAME`
|
|
90
90
|
|
|
@@ -98,26 +98,27 @@ ARGUMENTS
|
|
|
98
98
|
PROJECT-NAME Name of the project folder. This is not the name of the data pack.
|
|
99
99
|
|
|
100
100
|
OPTIONS
|
|
101
|
-
-
|
|
102
|
-
-
|
|
103
|
-
-
|
|
104
|
-
-
|
|
101
|
+
-c, --client-path=client-path The client path to write packs at.
|
|
102
|
+
-d, --pack-name=pack-name The name of the data pack.
|
|
103
|
+
-h, --help show CLI help
|
|
104
|
+
-n, --namespace=namespace The default namespace that will be used.
|
|
105
105
|
|
|
106
|
-
-r, --save-root
|
|
107
|
-
|
|
106
|
+
-r, --save-root Save the data pack & resource pack in the .minecraft/datapacks &
|
|
107
|
+
.minecraft/resource_packs folders. Not compatible with --world.
|
|
108
108
|
|
|
109
|
-
-
|
|
110
|
-
--custom-path.
|
|
109
|
+
-s, --server-path=server-path The server path to write the server-side packs at. Not compatible with --world.
|
|
111
110
|
|
|
112
|
-
--
|
|
111
|
+
-w, --world=world The world to save the packs in. Not compatible with --save-root or --server
|
|
113
112
|
|
|
114
|
-
--
|
|
113
|
+
--npm Use npm.
|
|
114
|
+
|
|
115
|
+
--yarn Use yarn instead of npm.
|
|
115
116
|
|
|
116
117
|
EXAMPLE
|
|
117
|
-
$ sand create my-
|
|
118
|
+
$ sand create my-pack
|
|
118
119
|
```
|
|
119
120
|
|
|
120
|
-
_See code: [src/commands/create.ts](https://github.com/TheMrZZ/sandstone-cli/blob/v0.
|
|
121
|
+
_See code: [src/commands/create.ts](https://github.com/TheMrZZ/sandstone-cli/blob/v0.6.1/src/commands/create.ts)_
|
|
121
122
|
|
|
122
123
|
## `sand help [COMMAND]`
|
|
123
124
|
|
|
@@ -159,11 +160,11 @@ EXAMPLES
|
|
|
159
160
|
$ sand update --cli --sandstone --skip
|
|
160
161
|
```
|
|
161
162
|
|
|
162
|
-
_See code: [src/commands/update.ts](https://github.com/TheMrZZ/sandstone-cli/blob/v0.
|
|
163
|
+
_See code: [src/commands/update.ts](https://github.com/TheMrZZ/sandstone-cli/blob/v0.6.1/src/commands/update.ts)_
|
|
163
164
|
|
|
164
165
|
## `sand watch PATH CONFIG-PATH`
|
|
165
166
|
|
|
166
|
-
Build the
|
|
167
|
+
Build the packs, and rebuild them on file change. ⛏
|
|
167
168
|
|
|
168
169
|
```
|
|
169
170
|
USAGE
|
|
@@ -174,7 +175,7 @@ ARGUMENTS
|
|
|
174
175
|
CONFIG-PATH [default: .] Path of the sandstone.config.ts folder.
|
|
175
176
|
|
|
176
177
|
OPTIONS
|
|
177
|
-
-d, --dry Do not save the
|
|
178
|
+
-d, --dry Do not save the pack. Mostly useful with `verbose`.
|
|
178
179
|
-h, --help show CLI help
|
|
179
180
|
-p, --production Runs Sandstone in production mode. This sets process.env.SANDSTONE_ENV to "production".
|
|
180
181
|
-v, --verbose Log all resulting resources: functions, advancements...
|
|
@@ -182,23 +183,23 @@ OPTIONS
|
|
|
182
183
|
--autoReload=port Automatically reload your data pack in-game. Requires to open the world to LAN with
|
|
183
184
|
cheats enabled, and to specify the port.
|
|
184
185
|
|
|
186
|
+
--clientPath=clientPath Path of the client folder. Override the value specified in the configuration file.
|
|
187
|
+
|
|
185
188
|
--description=description Description of the data pack. Override the value specified in the configuration file.
|
|
186
189
|
|
|
187
190
|
--formatVersion=formatVersion Pack format version. Override the value specified in the configuration file.
|
|
188
191
|
|
|
189
192
|
--fullTrace Show the full stack trace on errors.
|
|
190
193
|
|
|
191
|
-
--minecraftPath=minecraftPath Path of the .minecraft folder. Override the value specified in the configuration file.
|
|
192
|
-
|
|
193
194
|
--name=name Name of the data pack. Override the value specified in the configuration file.
|
|
194
195
|
|
|
195
196
|
--namespace=namespace The default namespace. Override the value specified in the configuration file.
|
|
196
197
|
|
|
197
|
-
--
|
|
198
|
+
--root Save the data pack & resource pack in the .minecraft/datapacks &
|
|
199
|
+
.minecraft/resource_packs folders. Override the value specified in the configuration
|
|
198
200
|
file.
|
|
199
201
|
|
|
200
|
-
--
|
|
201
|
-
in the configuration file.
|
|
202
|
+
--serverPath=serverPath Path of the server folder. Override the value specified in the configuration file.
|
|
202
203
|
|
|
203
204
|
--strictErrors Stop data pack compilation on type errors.
|
|
204
205
|
|
|
@@ -211,5 +212,5 @@ EXAMPLES
|
|
|
211
212
|
$ sand watch --verbose --dry
|
|
212
213
|
```
|
|
213
214
|
|
|
214
|
-
_See code: [src/commands/watch.ts](https://github.com/TheMrZZ/sandstone-cli/blob/v0.
|
|
215
|
+
_See code: [src/commands/watch.ts](https://github.com/TheMrZZ/sandstone-cli/blob/v0.6.1/src/commands/watch.ts)_
|
|
215
216
|
<!-- commandsstop -->
|
package/bin/run
CHANGED
|
File without changes
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { ProjectFolders } from '
|
|
1
|
+
import { ProjectFolders } from '../utils';
|
|
2
2
|
export declare type BuildOptions = {
|
|
3
3
|
world?: string;
|
|
4
4
|
root?: boolean;
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
clientPath?: string;
|
|
6
|
+
serverPath?: string;
|
|
7
|
+
ssh?: string;
|
|
7
8
|
namespace?: string;
|
|
8
9
|
name?: string;
|
|
9
10
|
description?: string;
|
|
@@ -19,5 +20,7 @@ export declare type BuildOptions = {
|
|
|
19
20
|
* @param options The options to build the project with.
|
|
20
21
|
*
|
|
21
22
|
* @param projectFolder The folder of the project. It needs a sandstone.config.ts, and it or one of its parent needs a package.json.
|
|
23
|
+
*
|
|
24
|
+
* @param changedFiles The files that changed since the last build.
|
|
22
25
|
*/
|
|
23
|
-
export declare function buildProject(options: BuildOptions, folders: ProjectFolders): Promise<void>;
|
|
26
|
+
export declare function buildProject(options: BuildOptions, folders: ProjectFolders, resourceTypes: string[], changedFiles?: string[]): Promise<void>;
|
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
22
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
|
+
};
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.buildProject = void 0;
|
|
26
|
+
const path_1 = __importDefault(require("path"));
|
|
27
|
+
const os = __importStar(require("os"));
|
|
28
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
29
|
+
const util_1 = require("util");
|
|
30
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
31
|
+
const pretty_error_1 = __importDefault(require("pretty-error"));
|
|
32
|
+
const klaw_1 = __importDefault(require("klaw"));
|
|
33
|
+
const madge_1 = __importDefault(require("madge"));
|
|
34
|
+
const graph_1 = require("./graph");
|
|
35
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
36
|
+
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
37
|
+
const pe = new pretty_error_1.default();
|
|
38
|
+
// Return the hash of a string
|
|
39
|
+
function hash(stringToHash) {
|
|
40
|
+
return crypto_1.default.createHash('md5').update(stringToHash).digest('hex');
|
|
41
|
+
}
|
|
42
|
+
// Recursively create a directory, without failing if it already exists
|
|
43
|
+
async function mkDir(dirPath) {
|
|
44
|
+
try {
|
|
45
|
+
await new Promise((resolve, reject) => {
|
|
46
|
+
fs_extra_1.default.mkdir(dirPath, { recursive: true }, (err) => {
|
|
47
|
+
if (err)
|
|
48
|
+
reject(err);
|
|
49
|
+
resolve();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
// Directory already exists
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
let cache = {};
|
|
58
|
+
const dependenciesCache = new graph_1.DependencyGraph({});
|
|
59
|
+
const fileResources = new Map();
|
|
60
|
+
function getNewModules(dependenciesGraph, rawFiles, projectFolder) {
|
|
61
|
+
const rawFilesPath = rawFiles.map(({ path }) => path);
|
|
62
|
+
// Get only the new modules
|
|
63
|
+
const newModules = [...dependenciesGraph.nodes.values()].filter((node) => rawFilesPath.includes(path_1.default.join(projectFolder, node.name)));
|
|
64
|
+
// Get their dependants, as a set to avoid duplicates
|
|
65
|
+
const newModulesDependencies = new Set(newModules.flatMap((node) => [...node.getDependsOn({ recursive: true, includeSelf: true })]));
|
|
66
|
+
// Sort them by number of dependencies, and return them
|
|
67
|
+
return [...newModulesDependencies].sort((a, b) => a.getDependencies({ recursive: true }).size - b.getDependencies({ recursive: true }).size);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Returns a set of all values present in set1 and not present in set2.
|
|
71
|
+
*/
|
|
72
|
+
function diffSet(set1, set2) {
|
|
73
|
+
return [...set1].filter((element) => !set2.has(element));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Returns a map of all key/value present in map1 and not present in map2.
|
|
77
|
+
*/
|
|
78
|
+
function diffMap(map1, map2) {
|
|
79
|
+
return new Map([...map1.entries()].filter(([key, value]) => !map2.has(key)));
|
|
80
|
+
}
|
|
81
|
+
function diffResources(tree1, tree2) {
|
|
82
|
+
return tree1.diff(tree2);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
*
|
|
86
|
+
* @param worldName The name of the world
|
|
87
|
+
* @param minecraftPath The optional location of the .minecraft folder.
|
|
88
|
+
* If left unspecified, the .minecraft will be found automatically.
|
|
89
|
+
*/
|
|
90
|
+
async function getClientWorldPath(worldName, minecraftPath = undefined) {
|
|
91
|
+
let mcPath;
|
|
92
|
+
if (minecraftPath) {
|
|
93
|
+
mcPath = minecraftPath;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
mcPath = await getClientPath();
|
|
97
|
+
}
|
|
98
|
+
const savesPath = path_1.default.join(mcPath, 'saves');
|
|
99
|
+
const worldPath = path_1.default.join(savesPath, worldName);
|
|
100
|
+
if (!fs_extra_1.default.existsSync(worldPath)) {
|
|
101
|
+
const existingWorlds = (await fs_extra_1.default.readdir(savesPath, { withFileTypes: true })).filter((f) => f.isDirectory).map((f) => f.name);
|
|
102
|
+
throw new Error(`Unable to locate the "${worldPath}" folder. Word ${worldName} does not exists. List of existing worlds: ${JSON.stringify(existingWorlds, null, 2)}`);
|
|
103
|
+
}
|
|
104
|
+
return worldPath;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get the .minecraft path
|
|
108
|
+
*/
|
|
109
|
+
async function getClientPath() {
|
|
110
|
+
function getMCPath() {
|
|
111
|
+
switch (os.platform()) {
|
|
112
|
+
case 'win32':
|
|
113
|
+
return path_1.default.join(os.homedir(), 'AppData/Roaming/.minecraft');
|
|
114
|
+
case 'darwin':
|
|
115
|
+
return path_1.default.join(os.homedir(), 'Library/Application Support/minecraft');
|
|
116
|
+
case 'linux':
|
|
117
|
+
default:
|
|
118
|
+
return path_1.default.join(os.homedir(), '.minecraft');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const mcPath = getMCPath();
|
|
122
|
+
if (!await fs_extra_1.default.stat(mcPath)) {
|
|
123
|
+
throw new Error('Unable to locate the .minecraft folder. Please specify it manually.');
|
|
124
|
+
}
|
|
125
|
+
return mcPath;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Build the project, but might throw errors.
|
|
129
|
+
*
|
|
130
|
+
* @param cliOptions The options to build the project with.
|
|
131
|
+
*
|
|
132
|
+
* @param projectFolder The folder of the project. It needs a sandstone.config.ts, and it or one of its parent needs a package.json.
|
|
133
|
+
*/
|
|
134
|
+
async function _buildProject(cliOptions, { absProjectFolder, rootFolder, sandstoneConfigFolder }, resourceTypes, changedFiles) {
|
|
135
|
+
var _a, _b, _c, _d, _e;
|
|
136
|
+
const sandstoneLocation = path_1.default.join(rootFolder, 'node_modules/sandstone/');
|
|
137
|
+
// First, read sandstone.config.ts to get all properties
|
|
138
|
+
const sandstoneConfig = require(path_1.default.join(sandstoneConfigFolder, 'sandstone.config.ts')).default;
|
|
139
|
+
const { saveOptions, scripts } = sandstoneConfig;
|
|
140
|
+
const outputFolder = path_1.default.join(rootFolder, '.sandstone', 'output');
|
|
141
|
+
/// OPTIONS ///
|
|
142
|
+
const clientPath = !cliOptions.production ? (cliOptions.clientPath || saveOptions.clientPath || await getClientPath()) : undefined;
|
|
143
|
+
const server = !cliOptions.production && (cliOptions.serverPath || saveOptions.serverPath || cliOptions.ssh || saveOptions.ssh) ? await (async () => {
|
|
144
|
+
if (cliOptions.ssh || saveOptions.ssh) {
|
|
145
|
+
const sshOptions = JSON.stringify(await fs_extra_1.default.readFile(cliOptions.ssh || saveOptions.ssh, 'utf8'));
|
|
146
|
+
// TODO: implement SFTP
|
|
147
|
+
return {
|
|
148
|
+
readFile: async (relativePath, encoding = 'utf8') => { },
|
|
149
|
+
writeFile: async (relativePath, contents) => { },
|
|
150
|
+
remove: async (relativePath) => { },
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const serverPath = cliOptions.serverPath || saveOptions.serverPath;
|
|
154
|
+
return {
|
|
155
|
+
readFile: async (relativePath, encoding = 'utf8') => await fs_extra_1.default.readFile(path_1.default.join(serverPath, relativePath), encoding),
|
|
156
|
+
writeFile: async (relativePath, contents) => {
|
|
157
|
+
if (contents === undefined) {
|
|
158
|
+
await fs_extra_1.default.unlink(path_1.default.join(serverPath, relativePath));
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
await fs_extra_1.default.writeFile(path_1.default.join(serverPath, relativePath), contents);
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
remove: async (relativePath) => await fs_extra_1.default.remove(path_1.default.join(serverPath, relativePath))
|
|
165
|
+
};
|
|
166
|
+
})() : undefined;
|
|
167
|
+
let worldName = cliOptions.world || saveOptions.world;
|
|
168
|
+
// Make sure the world exists
|
|
169
|
+
if (worldName && !cliOptions.production) {
|
|
170
|
+
await getClientWorldPath(worldName, clientPath);
|
|
171
|
+
}
|
|
172
|
+
const root = cliOptions.root !== undefined ? cliOptions.root : saveOptions.root;
|
|
173
|
+
const packName = (_a = cliOptions.name) !== null && _a !== void 0 ? _a : sandstoneConfig.name;
|
|
174
|
+
if (worldName && root) {
|
|
175
|
+
throw new Error(`Expected only 'world' or 'root'. Got both.`);
|
|
176
|
+
}
|
|
177
|
+
// Important /!\: The below if statements, which set environment variables, must run before importing any Sandstone file.
|
|
178
|
+
// Set the pack ID environment variable
|
|
179
|
+
// Set production/development mode
|
|
180
|
+
if (cliOptions.production) {
|
|
181
|
+
process.env.SANDSTONE_ENV = 'production';
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
process.env.SANDSTONE_ENV = 'development';
|
|
185
|
+
}
|
|
186
|
+
if (sandstoneConfig.packUid) {
|
|
187
|
+
process.env.PACK_UID = sandstoneConfig.packUid;
|
|
188
|
+
}
|
|
189
|
+
// Set the namespace
|
|
190
|
+
const namespace = cliOptions.namespace || sandstoneConfig.namespace;
|
|
191
|
+
if (namespace) {
|
|
192
|
+
process.env.NAMESPACE = namespace;
|
|
193
|
+
}
|
|
194
|
+
const { onConflict } = sandstoneConfig;
|
|
195
|
+
if (onConflict) {
|
|
196
|
+
if (onConflict.default) {
|
|
197
|
+
process.env[`GENERAL_CONFLICT_STRATEGY`] = onConflict.default;
|
|
198
|
+
}
|
|
199
|
+
for (const resource of resourceTypes) {
|
|
200
|
+
if (onConflict[resource]) {
|
|
201
|
+
process.env[`${resource.toUpperCase()}_CONFLICT_STRATEGY`] = onConflict[resource];
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// JSON indentation
|
|
206
|
+
process.env.INDENTATION = saveOptions.indentation;
|
|
207
|
+
// Pack mcmeta
|
|
208
|
+
process.env.PACK_OPTIONS = JSON.stringify(sandstoneConfig.packs);
|
|
209
|
+
// Configure error display
|
|
210
|
+
if (!cliOptions.fullTrace) {
|
|
211
|
+
pe.skipNodeFiles();
|
|
212
|
+
}
|
|
213
|
+
/// IMPORTING USER CODE ///
|
|
214
|
+
// The configuration is ready.
|
|
215
|
+
// Now, let's run the beforeAll script
|
|
216
|
+
await ((_b = scripts === null || scripts === void 0 ? void 0 : scripts.beforeAll) === null || _b === void 0 ? void 0 : _b.call(scripts));
|
|
217
|
+
// Finally, let's import all .ts & .js files under ./src.
|
|
218
|
+
let error = false;
|
|
219
|
+
// Get the list of all files
|
|
220
|
+
const rawFiles = [];
|
|
221
|
+
for await (const file of (0, klaw_1.default)(absProjectFolder)) {
|
|
222
|
+
rawFiles.push(file);
|
|
223
|
+
}
|
|
224
|
+
const changedFilesPaths = changedFiles === null || changedFiles === void 0 ? void 0 : changedFiles.map(file => ({ path: file }));
|
|
225
|
+
/**
|
|
226
|
+
* 1. Update dependency graphs
|
|
227
|
+
* 2. Delete all cache & resources for files dependent from the changed files
|
|
228
|
+
* 3. Import all changed files, & their dependents
|
|
229
|
+
* 4. Save only newly created resources
|
|
230
|
+
*/
|
|
231
|
+
const graph = await (0, madge_1.default)(rawFiles.map(f => f.path).filter(f => !f.endsWith('.json')), {
|
|
232
|
+
fileExtensions: ['.ts', '.cts', '.mts', '.tsx', '.js', '.jsx', '.cjs', '.mjs', '.json'],
|
|
233
|
+
includeNpm: false,
|
|
234
|
+
baseDir: absProjectFolder,
|
|
235
|
+
detectiveOptions: {
|
|
236
|
+
es6: {
|
|
237
|
+
skipTypeImports: true,
|
|
238
|
+
},
|
|
239
|
+
ts: {
|
|
240
|
+
skipTypeImports: true,
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
// This dependencies graph is only partial.
|
|
245
|
+
const dependenciesGraph = new graph_1.DependencyGraph(graph.obj());
|
|
246
|
+
// Update the global dependency graph by merging it with the new one.
|
|
247
|
+
dependenciesCache.merge(dependenciesGraph);
|
|
248
|
+
// Transform resolved dependents into a flat list of files, and sort them by their number of dependencies
|
|
249
|
+
const newModules = getNewModules(dependenciesCache, changedFilesPaths !== null && changedFilesPaths !== void 0 ? changedFilesPaths : rawFiles, absProjectFolder);
|
|
250
|
+
const { sandstonePack } = require(sandstoneLocation);
|
|
251
|
+
// If files changed, we need to clean the cache & delete the related resources
|
|
252
|
+
if (changedFiles) {
|
|
253
|
+
for (const node of newModules) {
|
|
254
|
+
// For each changed file, we need to reset the require cache
|
|
255
|
+
delete require.cache[path_1.default.join(absProjectFolder, node.name)];
|
|
256
|
+
// Then we need to delete all resources the file created
|
|
257
|
+
const oldResources = fileResources.get(node.name);
|
|
258
|
+
if (oldResources) {
|
|
259
|
+
for (const resource of oldResources.resources) {
|
|
260
|
+
sandstonePack.core.deleteResource(resource.path, resource.resourceType);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// Now, let's build the file & its dependents. First files to be built are the ones with less dependencies.
|
|
266
|
+
for (const node of newModules) {
|
|
267
|
+
const modulePath = path_1.default.join(absProjectFolder, node.name);
|
|
268
|
+
const currentResources = {
|
|
269
|
+
resources: new Set([...sandstonePack.core.resourceNodes]),
|
|
270
|
+
objectives: new Set([...sandstonePack.objectives.entries()])
|
|
271
|
+
};
|
|
272
|
+
// We have a module, let's require it!
|
|
273
|
+
const filePath = path_1.default.resolve(modulePath);
|
|
274
|
+
try {
|
|
275
|
+
// Sometimes, a file might not exist because it has been deleted.
|
|
276
|
+
if (await fs_extra_1.default.pathExists(filePath)) {
|
|
277
|
+
require(filePath);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
catch (e) {
|
|
281
|
+
logError(e, node.name);
|
|
282
|
+
error = true;
|
|
283
|
+
}
|
|
284
|
+
// Now, find the resources that were added by this file & store them.
|
|
285
|
+
// This will be used if those files are changed later.
|
|
286
|
+
const newResources = {
|
|
287
|
+
resources: diffResources(sandstonePack.core.resourceNodes, currentResources.resources),
|
|
288
|
+
objectives: diffSet(sandstonePack.objectives, currentResources.objectives),
|
|
289
|
+
};
|
|
290
|
+
fileResources.set(node.name, newResources);
|
|
291
|
+
}
|
|
292
|
+
if (error) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
/// SAVING RESULTS ///
|
|
296
|
+
// Setup the cache if it doesn't exist.
|
|
297
|
+
// This cache is here to avoid writing files on disk when they did not change.
|
|
298
|
+
const newCache = {};
|
|
299
|
+
if (cache === undefined) {
|
|
300
|
+
cache = {};
|
|
301
|
+
}
|
|
302
|
+
// Save the pack
|
|
303
|
+
// Run the beforeSave script (TODO: This is where sandstone-server will remove restart env vars)
|
|
304
|
+
await ((_c = scripts === null || scripts === void 0 ? void 0 : scripts.beforeSave) === null || _c === void 0 ? void 0 : _c.call(scripts));
|
|
305
|
+
const packTypes = await sandstonePack.save({
|
|
306
|
+
// Additional parameters
|
|
307
|
+
dryRun: cliOptions.dry,
|
|
308
|
+
verbose: cliOptions.verbose,
|
|
309
|
+
fileHandler: (_d = saveOptions.customFileHandler) !== null && _d !== void 0 ? _d : (async ({ relativePath, content, contentSummary }) => {
|
|
310
|
+
// We hash the relative path alongside the content to ensure unique hash.
|
|
311
|
+
const hashValue = hash(contentSummary + relativePath);
|
|
312
|
+
// Add to new cache.
|
|
313
|
+
newCache[relativePath] = hashValue;
|
|
314
|
+
if (cache[relativePath] === hashValue) {
|
|
315
|
+
// Already in cache - skip
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
// Not in cache: write to disk
|
|
319
|
+
const realPath = path_1.default.join(rootFolder, relativePath);
|
|
320
|
+
await mkDir(path_1.default.dirname(realPath));
|
|
321
|
+
return await fs_extra_1.default.writeFile(realPath, content);
|
|
322
|
+
})
|
|
323
|
+
});
|
|
324
|
+
async function archiveOutput(packType, outputPath) {
|
|
325
|
+
const archive = new adm_zip_1.default();
|
|
326
|
+
await archive.addLocalFolderPromise(outputPath, {});
|
|
327
|
+
await archive.writeZipPromise(`${outputPath}.zip`, { overwrite: true });
|
|
328
|
+
}
|
|
329
|
+
// TODO: implement linking to make the cache more useful when not archiving.
|
|
330
|
+
if (!cliOptions.production) {
|
|
331
|
+
for (const packType of packTypes) {
|
|
332
|
+
const outputPath = path_1.default.join(rootFolder, '.sandstone/output/archives', `${packName}_${packType.type}`);
|
|
333
|
+
if (packType.handleOutput) {
|
|
334
|
+
await packType.handleOutput('output', async (relativePath, encoding = 'utf8') => await fs_extra_1.default.readFile(path_1.default.join(outputPath, relativePath), encoding), async (relativePath, contents) => {
|
|
335
|
+
if (contents === undefined) {
|
|
336
|
+
await fs_extra_1.default.unlink(path_1.default.join(outputPath, relativePath));
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
await fs_extra_1.default.writeFile(path_1.default.join(outputPath, relativePath), contents);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
if (packType.archiveOutput) {
|
|
344
|
+
archiveOutput(packType, outputPath);
|
|
345
|
+
}
|
|
346
|
+
// Handle client
|
|
347
|
+
if (!(server && packType.networkSides === 'server')) {
|
|
348
|
+
let fullClientPath;
|
|
349
|
+
if (worldName) {
|
|
350
|
+
fullClientPath = path_1.default.join(clientPath, packType.clientPath);
|
|
351
|
+
try {
|
|
352
|
+
fullClientPath = fullClientPath.replace('$packName$', packName);
|
|
353
|
+
}
|
|
354
|
+
catch { }
|
|
355
|
+
try {
|
|
356
|
+
fullClientPath = fullClientPath.replace('$worldName$', worldName);
|
|
357
|
+
}
|
|
358
|
+
catch { }
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
fullClientPath = path_1.default.join(clientPath, packType.rootPath);
|
|
362
|
+
try {
|
|
363
|
+
fullClientPath = fullClientPath.replace('$packName$', packName);
|
|
364
|
+
}
|
|
365
|
+
catch { }
|
|
366
|
+
}
|
|
367
|
+
if (packType.archiveOutput) {
|
|
368
|
+
await fs_extra_1.default.copyFile(`${outputPath}.zip`, `${fullClientPath}.zip`);
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
await fs_extra_1.default.remove(fullClientPath);
|
|
372
|
+
await fs_extra_1.default.copy(outputPath, fullClientPath);
|
|
373
|
+
}
|
|
374
|
+
if (packType.handleOutput) {
|
|
375
|
+
await packType.handleOutput('client', async (relativePath, encoding = 'utf8') => await fs_extra_1.default.readFile(path_1.default.join(clientPath, relativePath), encoding), async (relativePath, contents) => {
|
|
376
|
+
if (contents === undefined) {
|
|
377
|
+
fs_extra_1.default.unlink(path_1.default.join(clientPath, relativePath));
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
await fs_extra_1.default.writeFile(path_1.default.join(clientPath, relativePath), contents);
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// Handle server
|
|
386
|
+
if (server && (packType.networkSides === 'server' || packType.networkSides === 'both')) {
|
|
387
|
+
let serverPath = packType.serverPath;
|
|
388
|
+
try {
|
|
389
|
+
serverPath = serverPath.replace('$packName$', packName);
|
|
390
|
+
}
|
|
391
|
+
catch { }
|
|
392
|
+
if (packType.archiveOutput) {
|
|
393
|
+
await server.writeFile(await fs_extra_1.default.readFile(`${outputPath}.zip`, 'utf8'), `${serverPath}.zip`);
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
server.remove(serverPath);
|
|
397
|
+
for await (const file of (0, klaw_1.default)(outputPath)) {
|
|
398
|
+
await server.writeFile(path_1.default.join(serverPath, file.path.split(outputPath)[1]), await fs_extra_1.default.readFile(file.path));
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (packType.handleOutput) {
|
|
402
|
+
await packType.handleOutput('server', server.readFile, server.writeFile);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
for (const packType of packTypes) {
|
|
409
|
+
const outputPath = path_1.default.join(rootFolder, '.sandstone/output/archives', `${packName}_${packType.type}`);
|
|
410
|
+
if (packType.handleOutput) {
|
|
411
|
+
await packType.handleOutput('output', async (relativePath, encoding = 'utf8') => await fs_extra_1.default.readFile(path_1.default.join(outputPath, relativePath), encoding), async (relativePath, contents) => {
|
|
412
|
+
if (contents === undefined) {
|
|
413
|
+
await fs_extra_1.default.unlink(path_1.default.join(outputPath, relativePath));
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
await fs_extra_1.default.writeFile(path_1.default.join(outputPath, relativePath), contents);
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
if (packType.archiveOutput) {
|
|
421
|
+
archiveOutput(packType, outputPath);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
// Delete old files that aren't cached anymore
|
|
426
|
+
const oldFilesNames = new Set(Object.keys(cache));
|
|
427
|
+
Object.keys(newCache).forEach(name => oldFilesNames.delete(name));
|
|
428
|
+
await Promise.allSettled([...oldFilesNames.values()].map(name => (0, util_1.promisify)(fs_extra_1.default.rm)(path_1.default.join(outputFolder, name))));
|
|
429
|
+
// Override old cache
|
|
430
|
+
cache = newCache;
|
|
431
|
+
// Run the afterAll script
|
|
432
|
+
await ((_e = scripts === null || scripts === void 0 ? void 0 : scripts.afterAll) === null || _e === void 0 ? void 0 : _e.call(scripts));
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Build the project. Will log errors and never throw any.
|
|
436
|
+
*
|
|
437
|
+
* @param options The options to build the project with.
|
|
438
|
+
*
|
|
439
|
+
* @param projectFolder The folder of the project. It needs a sandstone.config.ts, and it or one of its parent needs a package.json.
|
|
440
|
+
*
|
|
441
|
+
* @param changedFiles The files that changed since the last build.
|
|
442
|
+
*/
|
|
443
|
+
async function buildProject(options, folders, resourceTypes, changedFiles) {
|
|
444
|
+
try {
|
|
445
|
+
await _buildProject(options, folders, resourceTypes, changedFiles);
|
|
446
|
+
}
|
|
447
|
+
catch (err) {
|
|
448
|
+
logError(err);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
exports.buildProject = buildProject;
|
|
452
|
+
function logError(err, file) {
|
|
453
|
+
if (err) {
|
|
454
|
+
if (file) {
|
|
455
|
+
console.error(' ' + chalk_1.default.bgRed.white('BuildError') + chalk_1.default.gray(':'), `While loading "${file}", the following error happened:\n`);
|
|
456
|
+
}
|
|
457
|
+
console.error(pe.render(err));
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
process.on('unhandledRejection', logError);
|
|
461
|
+
process.on('uncaughtException', logError);
|