sandstone-cli 0.6.0 → 0.6.2

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 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.6.0 linux-x64 node-v16.19.0
22
+ sandstone-cli/0.6.2 linux-x64 node-v16.19.1
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 datapack. ⛏
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 datapack. Mostly useful with `verbose`.
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
- --path=path The path to save the data pack at. Override the value specified in the configuration
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
- --root Save the data pack in the `.minecraft/datapacks` folder. Override the value specified
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.6.0/src/commands/build.ts)_
87
+ _See code: [src/commands/build.ts](https://github.com/TheMrZZ/sandstone-cli/blob/v0.6.2/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
- -d, --datapack-name=datapack-name The name of the data pack.
102
- -h, --help show CLI help
103
- -n, --namespace=namespace The default namespace that will be used.
104
- -p, --custom-path=custom-path The path to save the data pack at. Not compatible with --save-root and --world.
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 Save the data pack in the .minecraft/datapacks folder. Not compatible with --world
107
- and --custom-path.
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
- -w, --world=world The world to save the data pack in. Not compatible with --save-root and
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
- --npm Use npm.
111
+ -w, --world=world The world to save the packs in. Not compatible with --save-root or --server
113
112
 
114
- --yarn Use yarn instead of npm.
113
+ --npm Use npm.
114
+
115
+ --yarn Use yarn instead of npm.
115
116
 
116
117
  EXAMPLE
117
- $ sand create my-datapack
118
+ $ sand create my-pack
118
119
  ```
119
120
 
120
- _See code: [src/commands/create.ts](https://github.com/TheMrZZ/sandstone-cli/blob/v0.6.0/src/commands/create.ts)_
121
+ _See code: [src/commands/create.ts](https://github.com/TheMrZZ/sandstone-cli/blob/v0.6.2/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.6.0/src/commands/update.ts)_
163
+ _See code: [src/commands/update.ts](https://github.com/TheMrZZ/sandstone-cli/blob/v0.6.2/src/commands/update.ts)_
163
164
 
164
165
  ## `sand watch PATH CONFIG-PATH`
165
166
 
166
- Build the datapack, and rebuild it on file change. ⛏
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 datapack. Mostly useful with `verbose`.
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
- --path=path The path to save the data pack at. Override the value specified in the configuration
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
- --root Save the data pack in the `.minecraft/datapacks` folder. Override the value specified
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.6.0/src/commands/watch.ts)_
215
+ _See code: [src/commands/watch.ts](https://github.com/TheMrZZ/sandstone-cli/blob/v0.6.2/src/commands/watch.ts)_
215
216
  <!-- commandsstop -->
@@ -2,8 +2,9 @@ import { ProjectFolders } from '../utils';
2
2
  export declare type BuildOptions = {
3
3
  world?: string;
4
4
  root?: boolean;
5
- path?: string;
6
- minecraftPath?: string;
5
+ clientPath?: string;
6
+ serverPath?: string;
7
+ ssh?: string;
7
8
  namespace?: string;
8
9
  name?: string;
9
10
  description?: string;
@@ -1,10 +1,30 @@
1
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
+ };
2
21
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
23
  };
5
24
  Object.defineProperty(exports, "__esModule", { value: true });
6
25
  exports.buildProject = void 0;
7
26
  const path_1 = __importDefault(require("path"));
27
+ const os = __importStar(require("os"));
8
28
  const crypto_1 = __importDefault(require("crypto"));
9
29
  const util_1 = require("util");
10
30
  const fs_extra_1 = __importDefault(require("fs-extra"));
@@ -13,6 +33,8 @@ const klaw_1 = __importDefault(require("klaw"));
13
33
  const madge_1 = __importDefault(require("madge"));
14
34
  const graph_1 = require("./graph");
15
35
  const chalk_1 = __importDefault(require("chalk"));
36
+ const adm_zip_1 = __importDefault(require("adm-zip"));
37
+ const delete_empty_1 = __importDefault(require("delete-empty"));
16
38
  const pe = new pretty_error_1.default();
17
39
  // Return the hash of a string
18
40
  function hash(stringToHash) {
@@ -33,36 +55,9 @@ async function mkDir(dirPath) {
33
55
  // Directory already exists
34
56
  }
35
57
  }
36
- const cache = {};
58
+ let cache;
37
59
  const dependenciesCache = new graph_1.DependencyGraph({});
38
60
  const fileResources = new Map();
39
- /**
40
- * Recursively removes empty directories from the given directory.
41
- *
42
- * If the directory itself is empty, it is also removed.
43
- *
44
- * Code taken from: https://gist.github.com/jakub-g/5903dc7e4028133704a4
45
- *
46
- * @param {string} directory Path to the directory to clean up
47
- */
48
- async function removeEmptyDirectories(directory) {
49
- // lstat does not follow symlinks (in contrast to stat)
50
- const fileStats = await fs_extra_1.default.lstat(directory);
51
- if (!fileStats.isDirectory()) {
52
- return;
53
- }
54
- let fileNames = await fs_extra_1.default.readdir(directory);
55
- if (fileNames.length > 0) {
56
- const recursiveRemovalPromises = fileNames.map((fileName) => removeEmptyDirectories(path_1.default.join(directory, fileName)));
57
- await Promise.all(recursiveRemovalPromises);
58
- // re-evaluate fileNames; after deleting subdirectory
59
- // we may have parent directory empty now
60
- fileNames = await fs_extra_1.default.readdir(directory);
61
- }
62
- if (fileNames.length === 0) {
63
- await fs_extra_1.default.rmdir(directory);
64
- }
65
- }
66
61
  function getNewModules(dependenciesGraph, rawFiles, projectFolder) {
67
62
  const rawFilesPath = rawFiles.map(({ path }) => path);
68
63
  // Get only the new modules
@@ -85,76 +80,137 @@ function diffMap(map1, map2) {
85
80
  return new Map([...map1.entries()].filter(([key, value]) => !map2.has(key)));
86
81
  }
87
82
  function diffResources(tree1, tree2) {
88
- return tree1.diff(tree2);
83
+ return diffSet(tree1, tree2);
84
+ }
85
+ /**
86
+ *
87
+ * @param worldName The name of the world
88
+ * @param minecraftPath The optional location of the .minecraft folder.
89
+ * If left unspecified, the .minecraft will be found automatically.
90
+ */
91
+ async function getClientWorldPath(worldName, minecraftPath = undefined) {
92
+ let mcPath;
93
+ if (minecraftPath) {
94
+ mcPath = minecraftPath;
95
+ }
96
+ else {
97
+ mcPath = await getClientPath();
98
+ }
99
+ const savesPath = path_1.default.join(mcPath, 'saves');
100
+ const worldPath = path_1.default.join(savesPath, worldName);
101
+ if (!fs_extra_1.default.existsSync(worldPath)) {
102
+ const existingWorlds = (await fs_extra_1.default.readdir(savesPath, { withFileTypes: true })).filter((f) => f.isDirectory).map((f) => f.name);
103
+ throw new Error(`Unable to locate the "${worldPath}" folder. Word ${worldName} does not exists. List of existing worlds: ${JSON.stringify(existingWorlds, null, 2)}`);
104
+ }
105
+ return worldPath;
106
+ }
107
+ /**
108
+ * Get the .minecraft path
109
+ */
110
+ async function getClientPath() {
111
+ function getMCPath() {
112
+ switch (os.platform()) {
113
+ case 'win32':
114
+ return path_1.default.join(os.homedir(), 'AppData/Roaming/.minecraft');
115
+ case 'darwin':
116
+ return path_1.default.join(os.homedir(), 'Library/Application Support/minecraft');
117
+ case 'linux':
118
+ default:
119
+ return path_1.default.join(os.homedir(), '.minecraft');
120
+ }
121
+ }
122
+ const mcPath = getMCPath();
123
+ if (!await fs_extra_1.default.stat(mcPath)) {
124
+ throw new Error('Unable to locate the .minecraft folder. Please specify it manually.');
125
+ }
126
+ return mcPath;
89
127
  }
90
128
  /**
91
129
  * Build the project, but might throw errors.
92
130
  *
93
- * @param options The options to build the project with.
131
+ * @param cliOptions The options to build the project with.
94
132
  *
95
133
  * @param projectFolder The folder of the project. It needs a sandstone.config.ts, and it or one of its parent needs a package.json.
96
134
  */
97
- async function _buildProject(options, { absProjectFolder, rootFolder, sandstoneConfigFolder }, changedFiles) {
98
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
135
+ async function _buildProject(cliOptions, { absProjectFolder, rootFolder, sandstoneConfigFolder }, changedFiles) {
136
+ var _a, _b, _c, _d, _e, _f, _g;
99
137
  const sandstoneLocation = path_1.default.join(rootFolder, 'node_modules/sandstone/');
100
138
  // First, read sandstone.config.ts to get all properties
101
139
  const sandstoneConfig = require(path_1.default.join(sandstoneConfigFolder, 'sandstone.config.ts')).default;
102
140
  const { saveOptions, scripts } = sandstoneConfig;
141
+ const outputFolder = path_1.default.join(rootFolder, '.sandstone', 'output');
103
142
  /// OPTIONS ///
104
- // Check if the player overidded the save options
105
- const overrideSaveOptions = options.world || options.root || options.path;
106
- const world = overrideSaveOptions ? options.world : saveOptions.world;
107
- const root = overrideSaveOptions ? options.root : saveOptions.root;
108
- const customPath = overrideSaveOptions ? options.path : saveOptions.path;
109
- const minecraftPath = (_a = options.minecraftPath) !== null && _a !== void 0 ? _a : sandstoneConfig.minecraftPath;
110
- const dataPackName = (_b = options.name) !== null && _b !== void 0 ? _b : sandstoneConfig.name;
111
- if ([world, root, customPath].filter(x => x !== undefined).length > 1) {
112
- throw new Error(`Expected only 'world', 'root' or 'path'. Got at least two of them: world=${world}, root=${root}, path=${customPath}`);
143
+ const clientPath = !cliOptions.production ? (cliOptions.clientPath || saveOptions.clientPath || await getClientPath()) : undefined;
144
+ const server = !cliOptions.production && (cliOptions.serverPath || saveOptions.serverPath || cliOptions.ssh || saveOptions.ssh) ? await (async () => {
145
+ if (cliOptions.ssh || saveOptions.ssh) {
146
+ const sshOptions = JSON.stringify(await fs_extra_1.default.readFile(cliOptions.ssh || saveOptions.ssh, 'utf8'));
147
+ // TODO: implement SFTP
148
+ return {
149
+ readFile: async (relativePath, encoding = 'utf8') => { },
150
+ writeFile: async (relativePath, contents) => { },
151
+ remove: async (relativePath) => { },
152
+ };
153
+ }
154
+ const serverPath = cliOptions.serverPath || saveOptions.serverPath;
155
+ return {
156
+ readFile: async (relativePath, encoding = 'utf8') => await fs_extra_1.default.readFile(path_1.default.join(serverPath, relativePath), encoding),
157
+ writeFile: async (relativePath, contents) => {
158
+ if (contents === undefined) {
159
+ await fs_extra_1.default.unlink(path_1.default.join(serverPath, relativePath));
160
+ }
161
+ else {
162
+ await fs_extra_1.default.writeFile(path_1.default.join(serverPath, relativePath), contents);
163
+ }
164
+ },
165
+ remove: async (relativePath) => await fs_extra_1.default.remove(path_1.default.join(serverPath, relativePath))
166
+ };
167
+ })() : undefined;
168
+ let worldName = cliOptions.world || saveOptions.world;
169
+ // Make sure the world exists
170
+ if (worldName && !cliOptions.production) {
171
+ await getClientWorldPath(worldName, clientPath);
172
+ }
173
+ const root = cliOptions.root !== undefined ? cliOptions.root : saveOptions.root;
174
+ const packName = (_a = cliOptions.name) !== null && _a !== void 0 ? _a : sandstoneConfig.name;
175
+ if (worldName && root) {
176
+ throw new Error(`Expected only 'world' or 'root'. Got both.`);
113
177
  }
114
178
  // Important /!\: The below if statements, which set environment variables, must run before importing any Sandstone file.
115
179
  // Set the pack ID environment variable
116
180
  // Set production/development mode
117
- if (options.production) {
181
+ if (cliOptions.production) {
118
182
  process.env.SANDSTONE_ENV = 'production';
119
183
  }
120
184
  else {
121
185
  process.env.SANDSTONE_ENV = 'development';
122
186
  }
187
+ process.env.WORKING_DIR = absProjectFolder;
123
188
  if (sandstoneConfig.packUid) {
124
189
  process.env.PACK_UID = sandstoneConfig.packUid;
125
190
  }
126
191
  // Set the namespace
127
- const namespace = sandstoneConfig.namespace || options.namespace;
192
+ const namespace = cliOptions.namespace || sandstoneConfig.namespace;
128
193
  if (namespace) {
129
194
  process.env.NAMESPACE = namespace;
130
195
  }
131
- // Set conflict strategies
132
- function setStrategy(strategyName, value) {
133
- if (value) {
134
- process.env[`${strategyName.toUpperCase()}_CONFLICT_STRATEGY`] = value;
196
+ const { onConflict } = sandstoneConfig;
197
+ if (onConflict) {
198
+ for (const resource of Object.entries(onConflict)) {
199
+ process.env[`${resource[0].toUpperCase()}_CONFLICT_STRATEGY`] = resource[1];
135
200
  }
136
201
  }
137
- const { onConflict } = sandstoneConfig;
138
- setStrategy('general', onConflict === null || onConflict === void 0 ? void 0 : onConflict.default);
139
- setStrategy('advancement', onConflict === null || onConflict === void 0 ? void 0 : onConflict.advancement);
140
- setStrategy('loot_table', onConflict === null || onConflict === void 0 ? void 0 : onConflict.lootTable);
141
- setStrategy('mcfunction', onConflict === null || onConflict === void 0 ? void 0 : onConflict.mcFunction);
142
- setStrategy('predicate', onConflict === null || onConflict === void 0 ? void 0 : onConflict.predicate);
143
- setStrategy('recipe', onConflict === null || onConflict === void 0 ? void 0 : onConflict.recipe);
144
- setStrategy('tag', onConflict === null || onConflict === void 0 ? void 0 : onConflict.tag);
202
+ // JSON indentation
203
+ process.env.INDENTATION = saveOptions.indentation;
204
+ // Pack mcmeta
205
+ process.env.PACK_OPTIONS = JSON.stringify(sandstoneConfig.packs);
145
206
  // Configure error display
146
- if (!options.fullTrace) {
207
+ if (!cliOptions.fullTrace) {
147
208
  pe.skipNodeFiles();
148
209
  }
149
210
  /// IMPORTING USER CODE ///
150
211
  // The configuration is ready.
151
212
  // Now, let's run the beforeAll script
152
- const { getDestinationPath } = require(path_1.default.join(sandstoneLocation, 'datapack', 'saveDatapack'));
153
- const destinationPath = getDestinationPath(dataPackName, { world, asRootDatapack: root, customPath, minecraftPath });
154
- await ((_c = scripts === null || scripts === void 0 ? void 0 : scripts.beforeAll) === null || _c === void 0 ? void 0 : _c.call(scripts, {
155
- dataPackName,
156
- destination: destinationPath,
157
- }));
213
+ await ((_b = scripts === null || scripts === void 0 ? void 0 : scripts.beforeAll) === null || _b === void 0 ? void 0 : _b.call(scripts));
158
214
  // Finally, let's import all .ts & .js files under ./src.
159
215
  let error = false;
160
216
  // Get the list of all files
@@ -188,28 +244,17 @@ async function _buildProject(options, { absProjectFolder, rootFolder, sandstoneC
188
244
  dependenciesCache.merge(dependenciesGraph);
189
245
  // Transform resolved dependents into a flat list of files, and sort them by their number of dependencies
190
246
  const newModules = getNewModules(dependenciesCache, changedFilesPaths !== null && changedFilesPaths !== void 0 ? changedFilesPaths : rawFiles, absProjectFolder);
191
- const { savePack } = require(sandstoneLocation);
192
- const { dataPack } = require(sandstoneLocation + '/init');
247
+ const { sandstonePack } = require(sandstoneLocation);
193
248
  // If files changed, we need to clean the cache & delete the related resources
194
249
  if (changedFiles) {
195
250
  for (const node of newModules) {
196
- // For eached changed file, we need to reset the require cache
251
+ // For each changed file, we need to reset the require cache
197
252
  delete require.cache[path_1.default.join(absProjectFolder, node.name)];
198
253
  // Then we need to delete all resources the file created
199
254
  const oldResources = fileResources.get(node.name);
200
255
  if (oldResources) {
201
- const { resources, customResources, objectives, rootFunctions } = oldResources;
202
- for (const resource of resources) {
203
- dataPack.resources.deleteResource(resource.path, resource.resourceType);
204
- }
205
- for (const resource of customResources) {
206
- dataPack.customResources.delete(resource);
207
- }
208
- for (const objective of objectives.keys()) {
209
- dataPack.objectives.delete(objective);
210
- }
211
- for (const rootFunction of rootFunctions) {
212
- dataPack.rootFunctions.delete(rootFunction);
256
+ for (const resource of oldResources.resources) {
257
+ sandstonePack.core.deleteResource(resource.path, resource.resourceType);
213
258
  }
214
259
  }
215
260
  }
@@ -218,10 +263,8 @@ async function _buildProject(options, { absProjectFolder, rootFolder, sandstoneC
218
263
  for (const node of newModules) {
219
264
  const modulePath = path_1.default.join(absProjectFolder, node.name);
220
265
  const currentResources = {
221
- resources: dataPack.resources.clone(),
222
- objectives: new Map([...dataPack.objectives.entries()]),
223
- rootFunctions: new Set([...dataPack.rootFunctions]),
224
- customResources: new Set([...dataPack.customResources]),
266
+ resources: new Set([...sandstonePack.core.resourceNodes]),
267
+ objectives: new Set([...sandstonePack.objectives.entries()])
225
268
  };
226
269
  // We have a module, let's require it!
227
270
  const filePath = path_1.default.resolve(modulePath);
@@ -230,10 +273,6 @@ async function _buildProject(options, { absProjectFolder, rootFolder, sandstoneC
230
273
  if (await fs_extra_1.default.pathExists(filePath)) {
231
274
  require(filePath);
232
275
  }
233
- // Generate the mcfunctions
234
- for (const mcfunction of dataPack.rootFunctions) {
235
- await mcfunction.generate();
236
- }
237
276
  }
238
277
  catch (e) {
239
278
  logError(e, node.name);
@@ -242,10 +281,8 @@ async function _buildProject(options, { absProjectFolder, rootFolder, sandstoneC
242
281
  // Now, find the resources that were added by this file & store them.
243
282
  // This will be used if those files are changed later.
244
283
  const newResources = {
245
- resources: diffResources(dataPack.resources, currentResources.resources),
246
- customResources: diffSet(dataPack.customResources, currentResources.customResources),
247
- rootFunctions: diffSet(dataPack.rootFunctions, currentResources.rootFunctions),
248
- objectives: diffMap(dataPack.objectives, currentResources.objectives),
284
+ resources: diffResources(sandstonePack.core.resourceNodes, currentResources.resources),
285
+ objectives: diffSet(sandstonePack.objectives, currentResources.objectives),
249
286
  };
250
287
  fileResources.set(node.name, newResources);
251
288
  }
@@ -255,72 +292,223 @@ async function _buildProject(options, { absProjectFolder, rootFolder, sandstoneC
255
292
  /// SAVING RESULTS ///
256
293
  // Setup the cache if it doesn't exist.
257
294
  // This cache is here to avoid writing files on disk when they did not change.
258
- const newCache = {
259
- files: {}
260
- };
261
- if (cache[absProjectFolder] === undefined) {
262
- cache[absProjectFolder] = {
263
- files: {},
264
- };
295
+ const newCache = {};
296
+ const cacheFile = path_1.default.join(rootFolder, '.sandstone', 'cache.json');
297
+ if (cache === undefined) {
298
+ let oldCache;
299
+ try {
300
+ const fileRead = await fs_extra_1.default.readFile(cacheFile, 'utf8');
301
+ if (fileRead) {
302
+ oldCache = JSON.parse(fileRead);
303
+ }
304
+ }
305
+ catch { }
306
+ if (oldCache) {
307
+ cache = oldCache;
308
+ }
309
+ else {
310
+ cache = {};
311
+ }
265
312
  }
266
313
  // Save the pack
267
- // Run the beforeSave script
268
- await ((_d = scripts === null || scripts === void 0 ? void 0 : scripts.beforeSave) === null || _d === void 0 ? void 0 : _d.call(scripts, {
269
- dataPackName,
270
- destination: destinationPath,
271
- }));
272
- await savePack(dataPackName, {
273
- // Save location
274
- world: world,
275
- asRootDatapack: root,
276
- customPath: customPath,
277
- minecraftPath: minecraftPath,
278
- indentation: saveOptions.indentation,
279
- // Data pack mcmeta
280
- description: (_e = options.description) !== null && _e !== void 0 ? _e : sandstoneConfig.description,
281
- formatVersion: (_f = options.formatVersion) !== null && _f !== void 0 ? _f : sandstoneConfig.formatVersion,
314
+ // Run the beforeSave script (TODO: This is where sandstone-server will remove restart env vars)
315
+ await ((_c = scripts === null || scripts === void 0 ? void 0 : scripts.beforeSave) === null || _c === void 0 ? void 0 : _c.call(scripts));
316
+ const excludeOption = (_d = saveOptions.resources) === null || _d === void 0 ? void 0 : _d.exclude;
317
+ const fileExclusions = excludeOption ? {
318
+ generated: (excludeOption.generated || excludeOption),
319
+ existing: (excludeOption.existing || excludeOption)
320
+ } : false;
321
+ const fileHandlers = ((_e = saveOptions.resources) === null || _e === void 0 ? void 0 : _e.handle) || false;
322
+ const packTypes = await sandstonePack.save({
282
323
  // Additional parameters
283
- dryRun: options.dry,
284
- verbose: options.verbose,
285
- customFileHandler: (_g = saveOptions.customFileHandler) !== null && _g !== void 0 ? _g : (async ({ relativePath, content }) => {
286
- var _a;
287
- const realPath = path_1.default.join(destinationPath, relativePath);
288
- // We hash the real path alongside the content.
289
- // Therefore, if the real path change (for example, the user changed the resulting directory), the file will be recreated.
290
- const hashValue = hash(content + realPath);
291
- // Add to new cache. We use the relative path as key to make the cache lighter.
292
- newCache.files[relativePath] = hashValue;
293
- newCache.resultFolder = destinationPath;
294
- if (((_a = cache[absProjectFolder].files) === null || _a === void 0 ? void 0 : _a[realPath]) === hashValue) {
295
- // Already in cache - skip
296
- return;
324
+ dry: cliOptions.dry,
325
+ verbose: cliOptions.verbose,
326
+ fileHandler: (_f = saveOptions.customFileHandler) !== null && _f !== void 0 ? _f : (async (relativePath, content) => {
327
+ let pathPass = true;
328
+ if (fileExclusions && fileExclusions.generated) {
329
+ for (const exclude of fileExclusions.generated) {
330
+ if (!Array.isArray(exclude)) {
331
+ pathPass = !exclude.test(relativePath);
332
+ }
333
+ }
334
+ }
335
+ if (fileHandlers) {
336
+ for (const handler of fileHandlers) {
337
+ if (handler.path.test(relativePath)) {
338
+ content = await handler.callback(content);
339
+ }
340
+ }
341
+ }
342
+ if (pathPass) {
343
+ // We hash the relative path alongside the content to ensure unique hash.
344
+ const hashValue = hash(content + relativePath);
345
+ // Add to new cache.
346
+ newCache[relativePath] = hashValue;
347
+ if (cache[relativePath] === hashValue) {
348
+ // Already in cache - skip
349
+ return;
350
+ }
351
+ // Not in cache: write to disk
352
+ const realPath = path_1.default.join(outputFolder, relativePath);
353
+ await mkDir(path_1.default.dirname(realPath));
354
+ return await fs_extra_1.default.writeFile(realPath, content);
297
355
  }
298
- // Not in cache: write to disk
299
- await mkDir(path_1.default.dirname(realPath));
300
- return await fs_extra_1.default.writeFile(realPath, content);
301
356
  })
302
357
  });
303
- // Delete old files that aren't cached anymore
304
- const oldFilesNames = new Set(Object.keys(cache[absProjectFolder].files));
305
- Object.keys(newCache.files).forEach(name => oldFilesNames.delete(name));
306
- const previousResultFolder = (_h = cache === null || cache === void 0 ? void 0 : cache[absProjectFolder]) === null || _h === void 0 ? void 0 : _h.resultFolder;
307
- await Promise.allSettled([...oldFilesNames.values()].map(name => (0, util_1.promisify)(fs_extra_1.default.rm)(path_1.default.join(previousResultFolder !== null && previousResultFolder !== void 0 ? previousResultFolder : '', name))));
308
- // Delete all empty folders of previous directory
309
- if (previousResultFolder !== undefined) {
310
- try {
311
- await removeEmptyDirectories(previousResultFolder);
358
+ async function handleResources(packType) {
359
+ const working = path_1.default.join(rootFolder, 'resources', packType);
360
+ for await (const file of (0, klaw_1.default)(path_1.default.join(rootFolder, 'resources', packType), { filter: (_path) => {
361
+ const relativePath = path_1.default.join(packType, _path.split(working)[1]);
362
+ let pathPass = true;
363
+ if (fileExclusions && fileExclusions.existing) {
364
+ for (const exclude of fileExclusions.existing) {
365
+ pathPass = Array.isArray(exclude) ? !exclude[0].test(relativePath) : !exclude.test(relativePath);
366
+ }
367
+ }
368
+ return pathPass;
369
+ } })) {
370
+ const relativePath = path_1.default.join(packType, file.path.split(working)[1]);
371
+ try {
372
+ let content = await fs_extra_1.default.readFile(file.path);
373
+ if (fileHandlers) {
374
+ for (const handler of fileHandlers) {
375
+ if (handler.path.test(relativePath)) {
376
+ content = await handler.callback(content);
377
+ }
378
+ }
379
+ }
380
+ // We hash the relative path alongside the content to ensure unique hash.
381
+ const hashValue = hash(content + relativePath);
382
+ // Add to new cache.
383
+ newCache[relativePath] = hashValue;
384
+ if (cache[relativePath] !== hashValue) {
385
+ // Not in cache: write to disk
386
+ const realPath = path_1.default.join(outputFolder, relativePath);
387
+ await mkDir(path_1.default.dirname(realPath));
388
+ await fs_extra_1.default.writeFile(realPath, content);
389
+ }
390
+ }
391
+ catch (e) { }
312
392
  }
313
- catch (e) {
314
- // Previous directory was deleted by the user himself
393
+ }
394
+ async function archiveOutput(packType) {
395
+ const outputPath = path_1.default.join(rootFolder, '.sandstone/output/archives', `${packName}_${packType.type}`);
396
+ const archive = new adm_zip_1.default();
397
+ await archive.addLocalFolderPromise(outputPath, {});
398
+ await archive.writeZipPromise(`${outputPath}.zip`, { overwrite: true });
399
+ }
400
+ // TODO: implement linking to make the cache more useful when not archiving.
401
+ if (!cliOptions.production) {
402
+ for await (const _packType of packTypes) {
403
+ const packType = _packType[1];
404
+ const outputPath = path_1.default.join(rootFolder, '.sandstone/output', packType.type);
405
+ if (packType.handleOutput) {
406
+ await packType.handleOutput('output', async (relativePath, encoding = 'utf8') => await fs_extra_1.default.readFile(path_1.default.join(outputPath, relativePath), encoding), async (relativePath, contents) => {
407
+ if (contents === undefined) {
408
+ await fs_extra_1.default.unlink(path_1.default.join(outputPath, relativePath));
409
+ }
410
+ else {
411
+ await fs_extra_1.default.writeFile(path_1.default.join(outputPath, relativePath), contents);
412
+ }
413
+ });
414
+ }
415
+ handleResources(packType.type);
416
+ if (packType.archiveOutput) {
417
+ archiveOutput(packType);
418
+ }
419
+ // Handle client
420
+ if (!(server && packType.networkSides === 'server')) {
421
+ let fullClientPath;
422
+ if (worldName) {
423
+ fullClientPath = path_1.default.join(clientPath, packType.clientPath);
424
+ try {
425
+ fullClientPath = fullClientPath.replace('$packName$', packName);
426
+ }
427
+ catch { }
428
+ try {
429
+ fullClientPath = fullClientPath.replace('$worldName$', worldName);
430
+ }
431
+ catch { }
432
+ }
433
+ else {
434
+ fullClientPath = path_1.default.join(clientPath, packType.rootPath);
435
+ try {
436
+ fullClientPath = fullClientPath.replace('$packName$', packName);
437
+ }
438
+ catch { }
439
+ }
440
+ if (packType.archiveOutput) {
441
+ await fs_extra_1.default.copyFile(`${outputPath}.zip`, `${fullClientPath}.zip`);
442
+ }
443
+ else {
444
+ await fs_extra_1.default.remove(fullClientPath);
445
+ await fs_extra_1.default.copy(outputPath, fullClientPath);
446
+ }
447
+ if (packType.handleOutput) {
448
+ await packType.handleOutput('client', async (relativePath, encoding = 'utf8') => await fs_extra_1.default.readFile(path_1.default.join(clientPath, relativePath), encoding), async (relativePath, contents) => {
449
+ if (contents === undefined) {
450
+ fs_extra_1.default.unlink(path_1.default.join(clientPath, relativePath));
451
+ }
452
+ else {
453
+ await fs_extra_1.default.writeFile(path_1.default.join(clientPath, relativePath), contents);
454
+ }
455
+ });
456
+ }
457
+ }
458
+ // Handle server
459
+ if (server && (packType.networkSides === 'server' || packType.networkSides === 'both')) {
460
+ let serverPath = packType.serverPath;
461
+ try {
462
+ serverPath = serverPath.replace('$packName$', packName);
463
+ }
464
+ catch { }
465
+ if (packType.archiveOutput) {
466
+ await server.writeFile(await fs_extra_1.default.readFile(`${outputPath}.zip`, 'utf8'), `${serverPath}.zip`);
467
+ }
468
+ else {
469
+ server.remove(serverPath);
470
+ for await (const file of (0, klaw_1.default)(outputPath)) {
471
+ await server.writeFile(path_1.default.join(serverPath, file.path.split(outputPath)[1]), await fs_extra_1.default.readFile(file.path));
472
+ }
473
+ }
474
+ if (packType.handleOutput) {
475
+ await packType.handleOutput('server', server.readFile, server.writeFile);
476
+ }
477
+ }
478
+ }
479
+ }
480
+ else {
481
+ for await (const packType of packTypes) {
482
+ const outputPath = path_1.default.join(rootFolder, '.sandstone/output/archives', `${packName}_${packType.type}`);
483
+ if (packType.handleOutput) {
484
+ await packType.handleOutput('output', async (relativePath, encoding = 'utf8') => await fs_extra_1.default.readFile(path_1.default.join(outputPath, relativePath), encoding), async (relativePath, contents) => {
485
+ if (contents === undefined) {
486
+ await fs_extra_1.default.unlink(path_1.default.join(outputPath, relativePath));
487
+ }
488
+ else {
489
+ await fs_extra_1.default.writeFile(path_1.default.join(outputPath, relativePath), contents);
490
+ }
491
+ });
492
+ }
493
+ handleResources(packType.type);
494
+ if (packType.archiveOutput) {
495
+ archiveOutput(packType);
496
+ }
315
497
  }
316
498
  }
499
+ // Delete old files that aren't cached anymore
500
+ const oldFilesNames = new Set(Object.keys(cache));
501
+ Object.keys(newCache).forEach(name => oldFilesNames.delete(name));
502
+ await Promise.allSettled([...oldFilesNames.values()].map(name => {
503
+ return (0, util_1.promisify)(fs_extra_1.default.rm)(path_1.default.join(outputFolder, name));
504
+ }));
505
+ await (0, delete_empty_1.default)(outputFolder);
317
506
  // Override old cache
318
- cache[absProjectFolder] = newCache;
507
+ cache = newCache;
508
+ // Write the cache to disk
509
+ await fs_extra_1.default.writeFile(cacheFile, JSON.stringify(cache));
319
510
  // Run the afterAll script
320
- await ((_j = scripts === null || scripts === void 0 ? void 0 : scripts.afterAll) === null || _j === void 0 ? void 0 : _j.call(scripts, {
321
- dataPackName,
322
- destination: destinationPath,
323
- }));
511
+ await ((_g = scripts === null || scripts === void 0 ? void 0 : scripts.afterAll) === null || _g === void 0 ? void 0 : _g.call(scripts));
324
512
  }
325
513
  /**
326
514
  * Build the project. Will log errors and never throw any.
@@ -336,7 +524,7 @@ async function buildProject(options, folders, changedFiles) {
336
524
  await _buildProject(options, folders, changedFiles);
337
525
  }
338
526
  catch (err) {
339
- logError(err);
527
+ console.log(err);
340
528
  }
341
529
  }
342
530
  exports.buildProject = buildProject;
@@ -345,6 +533,7 @@ function logError(err, file) {
345
533
  if (file) {
346
534
  console.error(' ' + chalk_1.default.bgRed.white('BuildError') + chalk_1.default.gray(':'), `While loading "${file}", the following error happened:\n`);
347
535
  }
536
+ debugger;
348
537
  console.error(pe.render(err));
349
538
  }
350
539
  }
@@ -9,8 +9,8 @@ export default class Build extends Command {
9
9
  namespace: import("@oclif/command/lib/flags").IOptionFlag<string | undefined>;
10
10
  world: import("@oclif/command/lib/flags").IOptionFlag<string | undefined>;
11
11
  root: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
12
- path: import("@oclif/command/lib/flags").IOptionFlag<string | undefined>;
13
- minecraftPath: import("@oclif/command/lib/flags").IOptionFlag<string | undefined>;
12
+ clientPath: import("@oclif/command/lib/flags").IOptionFlag<string | undefined>;
13
+ serverPath: import("@oclif/command/lib/flags").IOptionFlag<string | undefined>;
14
14
  name: import("@oclif/command/lib/flags").IOptionFlag<string | undefined>;
15
15
  description: import("@oclif/command/lib/flags").IOptionFlag<string | undefined>;
16
16
  formatVersion: import("@oclif/parser/lib/flags").IOptionFlag<number | undefined>;
@@ -22,7 +22,7 @@ class Build extends command_1.Command {
22
22
  }
23
23
  }
24
24
  exports.default = Build;
25
- Build.description = 'Build the datapack. ⛏';
25
+ Build.description = 'Build the packs. ⛏';
26
26
  Build.examples = [
27
27
  '$ sand build',
28
28
  '$ sand build --verbose',
@@ -6,11 +6,12 @@ export default class Create extends Command {
6
6
  help: import("@oclif/parser/lib/flags").IBooleanFlag<void>;
7
7
  yarn: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
8
8
  npm: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
9
- 'datapack-name': flags.IOptionFlag<string | undefined>;
9
+ 'pack-name': flags.IOptionFlag<string | undefined>;
10
10
  namespace: flags.IOptionFlag<string | undefined>;
11
11
  'save-root': import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
12
12
  world: flags.IOptionFlag<string | undefined>;
13
- 'custom-path': flags.IOptionFlag<string | undefined>;
13
+ 'server-path': flags.IOptionFlag<string | undefined>;
14
+ 'client-path': flags.IOptionFlag<string | undefined>;
14
15
  };
15
16
  static args: {
16
17
  name: string;
@@ -28,7 +28,7 @@ class Create extends command_1.Command {
28
28
  const { args, flags } = this.parse(Create);
29
29
  const projectPath = path_1.default.resolve(args['project-name']);
30
30
  const projectName = path_1.default.basename(projectPath);
31
- const datapackName = await (0, utils_1.getFlagOrPrompt)(flags, 'datapack-name', {
31
+ const packName = await (0, utils_1.getFlagOrPrompt)(flags, 'pack-name', {
32
32
  message: 'Name of your data pack (can be changed later) >',
33
33
  type: 'input',
34
34
  default: projectName,
@@ -41,27 +41,27 @@ class Create extends command_1.Command {
41
41
  else if (flags.world) {
42
42
  saveOptions.world = flags.world;
43
43
  }
44
- else if (flags['custom-path']) {
45
- saveOptions.path = flags['custom-path'];
44
+ else if (flags['server-path']) {
45
+ saveOptions.serverPath = flags['server-path'];
46
46
  }
47
- else {
48
- // User didn't specify a way to save the file. Ask him.
47
+ else { // TODO: Add support for ssh
48
+ // User didn't specify a way to save the file. Ask them.
49
49
  const { saveChoice } = await inquirer_1.default.prompt({
50
50
  name: 'saveChoice',
51
51
  type: 'list',
52
- message: 'Where do you want your datapack to be saved (can be changed later)?',
52
+ message: 'Where do you want your packs to be saved (can be changed later)?',
53
53
  choices: [{
54
- name: 'In the root .minecraft/datapacks folder',
54
+ name: 'In the root client (.minecraft) folder',
55
55
  value: 'root',
56
- short: '.minecraft/datapacks folder',
56
+ short: 'Client folder',
57
57
  }, {
58
- name: 'In the datapacks folder of a world',
58
+ name: 'In a world',
59
59
  value: 'world',
60
- short: 'World datapacks folder',
60
+ short: 'World',
61
61
  }, {
62
- name: 'At a custom path',
63
- value: 'path',
64
- short: 'Custom path',
62
+ name: 'In a server',
63
+ value: 'server-path',
64
+ short: 'Server path',
65
65
  }],
66
66
  });
67
67
  if (saveChoice === 'root') {
@@ -69,22 +69,25 @@ class Create extends command_1.Command {
69
69
  }
70
70
  else if (saveChoice === 'world') {
71
71
  const { world } = await inquirer_1.default.prompt({
72
- name: 'world',
73
- message: 'What world do you want to save the Datapack in? >',
72
+ name: 'World',
73
+ message: 'What world do you want to save the packs in? >',
74
74
  type: 'list',
75
75
  choices: utils_1.getWorldsList,
76
76
  });
77
77
  saveOptions.world = world;
78
78
  }
79
- else {
80
- const { path } = await inquirer_1.default.prompt({
81
- name: 'path',
82
- message: 'Where do you want to save the data pack? Relative paths are accepted. >',
79
+ else { // TODO: Add native folder selector
80
+ const { serverPath } = await inquirer_1.default.prompt({
81
+ name: 'Server path',
82
+ message: 'Where is the server to save the packs in? Relative paths are accepted. >',
83
83
  type: 'input',
84
84
  });
85
- saveOptions.path = path;
85
+ saveOptions.serverPath = serverPath;
86
86
  }
87
87
  }
88
+ if (flags['client-path']) {
89
+ saveOptions.clientPath = flags['client-path'];
90
+ }
88
91
  const namespace = await (0, utils_1.getFlagOrPrompt)(flags, 'namespace', {
89
92
  message: 'Default namespace (can be changed later) >',
90
93
  default: 'default',
@@ -115,6 +118,7 @@ class Create extends command_1.Command {
115
118
  (0, child_process_1.execSync)('npm install sandstone', { cwd: projectPath });
116
119
  (0, child_process_1.execSync)('npm install --save-dev typescript @types/node sandstone-cli', { cwd: projectPath });
117
120
  }
121
+ // TODO: Make profiles for either packs or libraries
118
122
  // Merge with the package.json template
119
123
  const generatedPackage = JSON.parse(fs_1.default.readFileSync(path_1.default.join(projectPath, 'package.json')).toString());
120
124
  /** Remove the `main` property */
@@ -125,12 +129,16 @@ class Create extends command_1.Command {
125
129
  const templateFolder = path_1.default.join(__dirname, '../template/');
126
130
  await fs_extra_1.default.copy(templateFolder, projectPath);
127
131
  // Write the sandstone.json file
128
- fs_1.default.writeFileSync(path_1.default.join(projectPath, 'sandstone.config.ts'), `import type { SandstoneConfig } from 'sandstone'
132
+ fs_1.default.writeFileSync(path_1.default.join(projectPath, 'sandstone.config.ts'), `import type { DatapackConfig, SandstoneConfig } from 'sandstone'
129
133
 
130
134
  export default {
131
- name: ${toJson(datapackName)},
132
- description: ${toJson(['A ', { text: 'Sandstone', color: 'gold' }, ' data pack.'])},
133
- formatVersion: ${7},
135
+ name: ${toJson(packName)},
136
+ packs: {
137
+ datapack: {
138
+ description: ${toJson(['A ', { text: 'Sandstone', color: 'gold' }, ' data pack.'])},
139
+ packFormat: ${11},
140
+ } as DatapackConfig
141
+ },
134
142
  namespace: ${toJson(namespace)},
135
143
  packUid: ${toJson((0, nanoid_1.nanoid)(8))},
136
144
  saveOptions: ${toJson(Object.fromEntries(Object.entries(saveOptions).filter(([_, value]) => value !== undefined)))},
@@ -151,17 +159,18 @@ export default {
151
159
  exports.default = Create;
152
160
  Create.description = 'Create a new Sandstone project.';
153
161
  Create.examples = [
154
- '$ sand create my-datapack',
162
+ '$ sand create my-pack',
155
163
  ];
156
164
  Create.flags = {
157
165
  help: command_1.flags.help({ char: 'h' }),
158
166
  yarn: command_1.flags.boolean({ description: 'Use yarn instead of npm.', env: 'USE_YARN', exclusive: ['npm'] }),
159
167
  npm: command_1.flags.boolean({ description: 'Use npm.', env: 'USE_NPM', exclusive: ['yarn'] }),
160
- 'datapack-name': command_1.flags.string({ char: 'd', env: 'DATAPACK_NAME', description: 'The name of the data pack.' }),
168
+ 'pack-name': command_1.flags.string({ char: 'd', env: 'PACK_NAME', description: 'The name of the data pack.' }),
161
169
  namespace: command_1.flags.string({ char: 'n', env: 'NAMESPACE', description: 'The default namespace that will be used.' }),
162
- 'save-root': command_1.flags.boolean({ char: 'r', env: 'SAVE_ROOT', description: 'Save the data pack in the .minecraft/datapacks folder. Not compatible with --world and --custom-path.', exclusive: ['world', 'custom-path'] }),
163
- world: command_1.flags.string({ char: 'w', env: 'WORLD', description: 'The world to save the data pack in. Not compatible with --save-root and --custom-path.', exclusive: ['save-root', 'custom-path'] }),
164
- 'custom-path': command_1.flags.string({ char: 'p', env: 'CUSTOM_PATH', description: 'The path to save the data pack at. Not compatible with --save-root and --world.', exclusive: ['save-root', 'world'] }),
170
+ 'save-root': command_1.flags.boolean({ char: 'r', env: 'SAVE_ROOT', description: 'Save the data pack & resource pack in the .minecraft/datapacks & .minecraft/resource_packs folders. Not compatible with --world.', exclusive: ['world'] }),
171
+ world: command_1.flags.string({ char: 'w', env: 'WORLD', description: 'The world to save the packs in. Not compatible with --save-root or --server', exclusive: ['save-root', 'server'] }),
172
+ 'server-path': command_1.flags.string({ char: 's', env: 'SERVER_PATH', description: 'The server path to write the server-side packs at. Not compatible with --world.', exclusive: ['world'] }),
173
+ 'client-path': command_1.flags.string({ char: 'c', env: 'CLIENT_PATH', description: 'The client path to write packs at.' }),
165
174
  };
166
175
  Create.args = [{
167
176
  name: 'project-name',
@@ -9,8 +9,8 @@ export default class Watch extends Command {
9
9
  namespace: flags.IOptionFlag<string | undefined>;
10
10
  world: flags.IOptionFlag<string | undefined>;
11
11
  root: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
12
- path: flags.IOptionFlag<string | undefined>;
13
- minecraftPath: flags.IOptionFlag<string | undefined>;
12
+ clientPath: flags.IOptionFlag<string | undefined>;
13
+ serverPath: flags.IOptionFlag<string | undefined>;
14
14
  name: flags.IOptionFlag<string | undefined>;
15
15
  description: flags.IOptionFlag<string | undefined>;
16
16
  formatVersion: import("@oclif/parser/lib/flags").IOptionFlag<number | undefined>;
@@ -15,6 +15,7 @@ class Watch extends command_1.Command {
15
15
  let alreadyBuilding = false;
16
16
  let needRebuild = false;
17
17
  let client = null;
18
+ // TODO: add support for clients & resources that require restarts & world resets, sandstone-server should override the involved environment variables if mods are present that fix it
18
19
  if (flags.autoReload !== undefined) {
19
20
  try {
20
21
  client = (await require('minecraft-protocol')).createClient({
@@ -72,7 +73,7 @@ class Watch extends command_1.Command {
72
73
  }
73
74
  }
74
75
  exports.default = Watch;
75
- Watch.description = 'Build the datapack, and rebuild it on file change. ⛏';
76
+ Watch.description = 'Build the packs, and rebuild them on file change. ⛏';
76
77
  Watch.examples = [
77
78
  '$ sand watch',
78
79
  '$ sand watch --verbose',
@@ -80,13 +81,14 @@ Watch.examples = [
80
81
  ];
81
82
  Watch.flags = {
82
83
  help: command_1.flags.help({ char: 'h' }),
83
- dry: command_1.flags.boolean({ char: 'd', description: 'Do not save the datapack. Mostly useful with `verbose`.' }),
84
+ dry: command_1.flags.boolean({ char: 'd', description: 'Do not save the pack. Mostly useful with `verbose`.' }),
84
85
  verbose: command_1.flags.boolean({ char: 'v', description: 'Log all resulting resources: functions, advancements...' }),
85
86
  namespace: command_1.flags.string({ description: 'The default namespace. Override the value specified in the configuration file.' }),
86
87
  world: command_1.flags.string({ description: 'The world to save the data pack in. Override the value specified in the configuration file.' }),
87
- root: command_1.flags.boolean({ description: 'Save the data pack in the `.minecraft/datapacks` folder. Override the value specified in the configuration file.' }),
88
- path: command_1.flags.string({ description: 'The path to save the data pack at. Override the value specified in the configuration file.' }),
89
- minecraftPath: command_1.flags.string({ name: 'minecraft-path', description: 'Path of the .minecraft folder. Override the value specified in the configuration file.' }),
88
+ root: command_1.flags.boolean({ description: 'Save the data pack & resource pack in the .minecraft/datapacks & .minecraft/resource_packs folders. Override the value specified in the configuration file.' }),
89
+ clientPath: command_1.flags.string({ name: 'client-path', description: 'Path of the client folder. Override the value specified in the configuration file.' }),
90
+ serverPath: command_1.flags.string({ name: 'server-path', description: 'Path of the server folder. Override the value specified in the configuration file.' }),
91
+ // TODO: ssh
90
92
  name: command_1.flags.string({ description: 'Name of the data pack. Override the value specified in the configuration file.' }),
91
93
  description: command_1.flags.string({ description: 'Description of the data pack. Override the value specified in the configuration file.' }),
92
94
  formatVersion: command_1.flags.integer({ name: 'format', description: 'Pack format version. Override the value specified in the configuration file.' }),
@@ -1,6 +1,6 @@
1
1
  # Sandstone project
2
2
 
3
- To build the datapack, run:
3
+ To build the packs, run:
4
4
  ```ts
5
5
  npm run build
6
6
  // or
@@ -9,7 +9,7 @@ yarn build
9
9
  sand build
10
10
  ```
11
11
 
12
- To automatically rebuild the datapack on each change, run:
12
+ To automatically rebuild the packs on each change, run:
13
13
  ```ts
14
14
  npm run watch
15
15
  // or
@@ -1 +1 @@
1
- {"version":"0.6.0","commands":{"build":{"id":"build","description":"Build the datapack. ⛏","pluginName":"sandstone-cli","pluginType":"core","aliases":[],"examples":["$ sand build","$ sand build --verbose","$ sand build --verbose --dry"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"dry":{"name":"dry","type":"boolean","char":"d","description":"Do not save the datapack. Mostly useful with `verbose`.","allowNo":false},"verbose":{"name":"verbose","type":"boolean","char":"v","description":"Log all resulting resources: functions, advancements...","allowNo":false},"namespace":{"name":"namespace","type":"option","description":"The default namespace. Override the value specified in the configuration file."},"world":{"name":"world","type":"option","description":"The world to save the data pack in. Override the value specified in the configuration file."},"root":{"name":"root","type":"boolean","description":"Save the data pack in the `.minecraft/datapacks` folder. Override the value specified in the configuration file.","allowNo":false},"path":{"name":"path","type":"option","description":"The path to save the data pack at. Override the value specified in the configuration file."},"minecraftPath":{"name":"minecraftPath","type":"option","description":"Path of the .minecraft folder. Override the value specified in the configuration file."},"name":{"name":"name","type":"option","description":"Name of the data pack. Override the value specified in the configuration file."},"description":{"name":"description","type":"option","description":"Description of the data pack. Override the value specified in the configuration file."},"formatVersion":{"name":"formatVersion","type":"option","description":"Pack format version. Override the value specified in the configuration file."},"fullTrace":{"name":"fullTrace","type":"boolean","description":"Show the full stack trace on errors.","allowNo":false},"strictErrors":{"name":"strictErrors","type":"boolean","description":"Stop data pack compilation on type errors.","allowNo":false},"production":{"name":"production","type":"boolean","char":"p","description":"Runs Sandstone in production mode. This sets process.env.SANDSTONE_ENV to \"production\".","allowNo":false},"autoReload":{"name":"autoReload","type":"option","description":"Automatically reload your data pack in-game. Requires to open the world to LAN with cheats enabled, and to specify the port.","helpValue":"port"}},"args":[{"name":"path","description":"Path of the folder containing source files.","required":true,"default":"./src"},{"name":"config-path","description":"Path of the sandstone.config.ts folder.","required":true,"default":"."}]},"create":{"id":"create","description":"Create a new Sandstone project.","pluginName":"sandstone-cli","pluginType":"core","aliases":[],"examples":["$ sand create my-datapack"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"yarn":{"name":"yarn","type":"boolean","description":"Use yarn instead of npm.","allowNo":false},"npm":{"name":"npm","type":"boolean","description":"Use npm.","allowNo":false},"datapack-name":{"name":"datapack-name","type":"option","char":"d","description":"The name of the data pack."},"namespace":{"name":"namespace","type":"option","char":"n","description":"The default namespace that will be used."},"save-root":{"name":"save-root","type":"boolean","char":"r","description":"Save the data pack in the .minecraft/datapacks folder. Not compatible with --world and --custom-path.","allowNo":false},"world":{"name":"world","type":"option","char":"w","description":"The world to save the data pack in. Not compatible with --save-root and --custom-path."},"custom-path":{"name":"custom-path","type":"option","char":"p","description":"The path to save the data pack at. Not compatible with --save-root and --world."}},"args":[{"name":"project-name","description":"Name of the project folder. This is not the name of the data pack.","required":true}]},"update":{"id":"update","description":"Update Sandstone & Sandstone-CLI.","pluginName":"sandstone-cli","pluginType":"core","aliases":[],"examples":["$ sand update","$ sand update --cli","$ sand update --sandstone","$ sand update --cli --sandstone --skip"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"cli":{"name":"cli","type":"boolean","description":"Update the Sandstone CLI without asking.","allowNo":false},"sandstone":{"name":"sandstone","type":"boolean","description":"Update the current Sandstone version without asking.","allowNo":false},"skip":{"name":"skip","type":"boolean","description":"Skip all interactive prompts and refuse them.","allowNo":false},"yarn":{"name":"yarn","type":"boolean","description":"Use yarn to install the updates.","allowNo":false},"npm":{"name":"npm","type":"boolean","description":"Use npm to install the updates.","allowNo":false}},"args":[]},"watch":{"id":"watch","description":"Build the datapack, and rebuild it on file change. ⛏","pluginName":"sandstone-cli","pluginType":"core","aliases":[],"examples":["$ sand watch","$ sand watch --verbose","$ sand watch --verbose --dry"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"dry":{"name":"dry","type":"boolean","char":"d","description":"Do not save the datapack. Mostly useful with `verbose`.","allowNo":false},"verbose":{"name":"verbose","type":"boolean","char":"v","description":"Log all resulting resources: functions, advancements...","allowNo":false},"namespace":{"name":"namespace","type":"option","description":"The default namespace. Override the value specified in the configuration file."},"world":{"name":"world","type":"option","description":"The world to save the data pack in. Override the value specified in the configuration file."},"root":{"name":"root","type":"boolean","description":"Save the data pack in the `.minecraft/datapacks` folder. Override the value specified in the configuration file.","allowNo":false},"path":{"name":"path","type":"option","description":"The path to save the data pack at. Override the value specified in the configuration file."},"minecraftPath":{"name":"minecraftPath","type":"option","description":"Path of the .minecraft folder. Override the value specified in the configuration file."},"name":{"name":"name","type":"option","description":"Name of the data pack. Override the value specified in the configuration file."},"description":{"name":"description","type":"option","description":"Description of the data pack. Override the value specified in the configuration file."},"formatVersion":{"name":"formatVersion","type":"option","description":"Pack format version. Override the value specified in the configuration file."},"fullTrace":{"name":"fullTrace","type":"boolean","description":"Show the full stack trace on errors.","allowNo":false},"strictErrors":{"name":"strictErrors","type":"boolean","description":"Stop data pack compilation on type errors.","allowNo":false},"production":{"name":"production","type":"boolean","char":"p","description":"Runs Sandstone in production mode. This sets process.env.SANDSTONE_ENV to \"production\".","allowNo":false},"autoReload":{"name":"autoReload","type":"option","description":"Automatically reload your data pack in-game. Requires to open the world to LAN with cheats enabled, and to specify the port.","helpValue":"port"}},"args":[{"name":"path","description":"Path of the folder containing source files.","required":true,"default":"./src"},{"name":"config-path","description":"Path of the sandstone.config.ts folder.","required":true,"default":"."}]}}}
1
+ {"version":"0.6.2","commands":{"build":{"id":"build","description":"Build the packs. ⛏","pluginName":"sandstone-cli","pluginType":"core","aliases":[],"examples":["$ sand build","$ sand build --verbose","$ sand build --verbose --dry"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"dry":{"name":"dry","type":"boolean","char":"d","description":"Do not save the pack. Mostly useful with `verbose`.","allowNo":false},"verbose":{"name":"verbose","type":"boolean","char":"v","description":"Log all resulting resources: functions, advancements...","allowNo":false},"namespace":{"name":"namespace","type":"option","description":"The default namespace. Override the value specified in the configuration file."},"world":{"name":"world","type":"option","description":"The world to save the data pack in. Override the value specified in the configuration file."},"root":{"name":"root","type":"boolean","description":"Save the data pack & resource pack in the .minecraft/datapacks & .minecraft/resource_packs folders. Override the value specified in the configuration file.","allowNo":false},"clientPath":{"name":"clientPath","type":"option","description":"Path of the client folder. Override the value specified in the configuration file."},"serverPath":{"name":"serverPath","type":"option","description":"Path of the server folder. Override the value specified in the configuration file."},"name":{"name":"name","type":"option","description":"Name of the data pack. Override the value specified in the configuration file."},"description":{"name":"description","type":"option","description":"Description of the data pack. Override the value specified in the configuration file."},"formatVersion":{"name":"formatVersion","type":"option","description":"Pack format version. Override the value specified in the configuration file."},"fullTrace":{"name":"fullTrace","type":"boolean","description":"Show the full stack trace on errors.","allowNo":false},"strictErrors":{"name":"strictErrors","type":"boolean","description":"Stop data pack compilation on type errors.","allowNo":false},"production":{"name":"production","type":"boolean","char":"p","description":"Runs Sandstone in production mode. This sets process.env.SANDSTONE_ENV to \"production\".","allowNo":false},"autoReload":{"name":"autoReload","type":"option","description":"Automatically reload your data pack in-game. Requires to open the world to LAN with cheats enabled, and to specify the port.","helpValue":"port"}},"args":[{"name":"path","description":"Path of the folder containing source files.","required":true,"default":"./src"},{"name":"config-path","description":"Path of the sandstone.config.ts folder.","required":true,"default":"."}]},"create":{"id":"create","description":"Create a new Sandstone project.","pluginName":"sandstone-cli","pluginType":"core","aliases":[],"examples":["$ sand create my-pack"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"yarn":{"name":"yarn","type":"boolean","description":"Use yarn instead of npm.","allowNo":false},"npm":{"name":"npm","type":"boolean","description":"Use npm.","allowNo":false},"pack-name":{"name":"pack-name","type":"option","char":"d","description":"The name of the data pack."},"namespace":{"name":"namespace","type":"option","char":"n","description":"The default namespace that will be used."},"save-root":{"name":"save-root","type":"boolean","char":"r","description":"Save the data pack & resource pack in the .minecraft/datapacks & .minecraft/resource_packs folders. Not compatible with --world.","allowNo":false},"world":{"name":"world","type":"option","char":"w","description":"The world to save the packs in. Not compatible with --save-root or --server"},"server-path":{"name":"server-path","type":"option","char":"s","description":"The server path to write the server-side packs at. Not compatible with --world."},"client-path":{"name":"client-path","type":"option","char":"c","description":"The client path to write packs at."}},"args":[{"name":"project-name","description":"Name of the project folder. This is not the name of the data pack.","required":true}]},"update":{"id":"update","description":"Update Sandstone & Sandstone-CLI.","pluginName":"sandstone-cli","pluginType":"core","aliases":[],"examples":["$ sand update","$ sand update --cli","$ sand update --sandstone","$ sand update --cli --sandstone --skip"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"cli":{"name":"cli","type":"boolean","description":"Update the Sandstone CLI without asking.","allowNo":false},"sandstone":{"name":"sandstone","type":"boolean","description":"Update the current Sandstone version without asking.","allowNo":false},"skip":{"name":"skip","type":"boolean","description":"Skip all interactive prompts and refuse them.","allowNo":false},"yarn":{"name":"yarn","type":"boolean","description":"Use yarn to install the updates.","allowNo":false},"npm":{"name":"npm","type":"boolean","description":"Use npm to install the updates.","allowNo":false}},"args":[]},"watch":{"id":"watch","description":"Build the packs, and rebuild them on file change. ⛏","pluginName":"sandstone-cli","pluginType":"core","aliases":[],"examples":["$ sand watch","$ sand watch --verbose","$ sand watch --verbose --dry"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"dry":{"name":"dry","type":"boolean","char":"d","description":"Do not save the pack. Mostly useful with `verbose`.","allowNo":false},"verbose":{"name":"verbose","type":"boolean","char":"v","description":"Log all resulting resources: functions, advancements...","allowNo":false},"namespace":{"name":"namespace","type":"option","description":"The default namespace. Override the value specified in the configuration file."},"world":{"name":"world","type":"option","description":"The world to save the data pack in. Override the value specified in the configuration file."},"root":{"name":"root","type":"boolean","description":"Save the data pack & resource pack in the .minecraft/datapacks & .minecraft/resource_packs folders. Override the value specified in the configuration file.","allowNo":false},"clientPath":{"name":"clientPath","type":"option","description":"Path of the client folder. Override the value specified in the configuration file."},"serverPath":{"name":"serverPath","type":"option","description":"Path of the server folder. Override the value specified in the configuration file."},"name":{"name":"name","type":"option","description":"Name of the data pack. Override the value specified in the configuration file."},"description":{"name":"description","type":"option","description":"Description of the data pack. Override the value specified in the configuration file."},"formatVersion":{"name":"formatVersion","type":"option","description":"Pack format version. Override the value specified in the configuration file."},"fullTrace":{"name":"fullTrace","type":"boolean","description":"Show the full stack trace on errors.","allowNo":false},"strictErrors":{"name":"strictErrors","type":"boolean","description":"Stop data pack compilation on type errors.","allowNo":false},"production":{"name":"production","type":"boolean","char":"p","description":"Runs Sandstone in production mode. This sets process.env.SANDSTONE_ENV to \"production\".","allowNo":false},"autoReload":{"name":"autoReload","type":"option","description":"Automatically reload your data pack in-game. Requires to open the world to LAN with cheats enabled, and to specify the port.","helpValue":"port"}},"args":[{"name":"path","description":"Path of the folder containing source files.","required":true,"default":"./src"},{"name":"config-path","description":"Path of the sandstone.config.ts folder.","required":true,"default":"."}]}}}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sandstone-cli",
3
3
  "description": "The CLI for Sandstone - the data pack creation library.",
4
- "version": "0.6.0",
4
+ "version": "0.6.2",
5
5
  "contributors": [
6
6
  {
7
7
  "name": "TheMrZZ - Florian ERNST",
@@ -22,10 +22,14 @@
22
22
  "@oclif/config": "^1",
23
23
  "@oclif/plugin-help": "^3",
24
24
  "@oclif/plugin-warn-if-update-available": "^1.7.0",
25
- "@types/node": "^18.11.18",
25
+ "@types/adm-zip": "^0.5.0",
26
+ "@types/delete-empty": "^3.0.2",
27
+ "@types/node": "^18.14.0",
26
28
  "@types/semver": "^7.3.4",
29
+ "adm-zip": "^0.5.10",
27
30
  "chalk": "^4.1.0",
28
31
  "chokidar": "^3.4.3",
32
+ "delete-empty": "^3.0.0",
29
33
  "fs-extra": "^9.0.1",
30
34
  "inquirer": "^7.3.3",
31
35
  "klaw": "^3.0.0",
@@ -42,7 +46,7 @@
42
46
  "@oclif/dev-cli": "^1",
43
47
  "@types/fs-extra": "^9.0.4",
44
48
  "@types/inquirer": "^7.3.1",
45
- "@types/klaw": "^3.0.1",
49
+ "@types/klaw": "^3.0.3",
46
50
  "@types/lodash.debounce": "^4.0.6",
47
51
  "@types/madge": "^5.0.0",
48
52
  "@types/nodemon": "^1.19.0",