@microtronics/studio-cli 0.54.0 → 0.56.0

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.
@@ -1897,35 +1897,39 @@ export declare namespace Dependencies {
1897
1897
  * @param cwd
1898
1898
  * @param fs
1899
1899
  * @param libraryName
1900
+ * @param logger
1900
1901
  */
1901
- export function removeExisting(cwd: URI, fs: LocalFS, libraryName: string): Promise<void>;
1902
+ export function removeExisting(cwd: URI, fs: LocalFS, logger: Log.Logger, libraryName: string): Promise<void>;
1902
1903
  /**
1903
1904
  * Extract the given library.tar.gz array buffer
1904
1905
  * @param cwd
1905
1906
  * @param fs
1907
+ * @param logger
1906
1908
  * @param libraryName
1907
1909
  * @param archiveBuffer
1908
1910
  */
1909
- export function extractLibraryContent(cwd: URI, fs: LocalFS, libraryName: string, archiveBuffer: ArrayBuffer): Promise<void>;
1911
+ export function extractLibraryContent(cwd: URI, fs: LocalFS, logger: Log.Logger, libraryName: string, archiveBuffer: ArrayBuffer): Promise<void>;
1910
1912
  /**
1911
1913
  * Install the given library with the version that the semver range accepts
1912
1914
  * @param cwd
1913
1915
  * @param fs
1916
+ * @param logger
1914
1917
  * @param env
1915
1918
  * @param token
1916
1919
  * @param publisherIdName
1917
1920
  * @param version
1918
1921
  * @param development - install as development dependency
1919
1922
  */
1920
- export function installDependency(cwd: URI, fs: LocalFS, env: Globals.ENV, token: string | null, publisherIdName: string, version: string | null, development?: boolean): Promise<undefined>;
1923
+ export function installDependency(cwd: URI, fs: LocalFS, logger: Log.Logger, env: Globals.ENV, token: string | null, publisherIdName: string, version: string | null, development?: boolean): Promise<undefined>;
1921
1924
  /**
1922
1925
  * Install all dependencies that are referenced within the studio manifest
1923
1926
  * @param cwd
1924
1927
  * @param fs
1928
+ * @param logger
1925
1929
  * @param env
1926
1930
  * @param token
1927
1931
  */
1928
- export function installDependencies(cwd: URI, fs: LocalFS, env: Globals.ENV, token: string | null): Promise<void>;
1932
+ export function installDependencies(cwd: URI, fs: LocalFS, logger: Log.Logger, env: Globals.ENV, token: string | null): Promise<void>;
1929
1933
  /**
1930
1934
  * Uninstall the given library
1931
1935
  * Validates if the library is any sub-dependency of any other library
@@ -1934,7 +1938,15 @@ export declare namespace Dependencies {
1934
1938
  * @param publisherId
1935
1939
  * @param libraryName
1936
1940
  */
1937
- export function uninstallDependency(cwd: URI, fs: LocalFS, publisherId: string, libraryName: string): Promise<void>;
1941
+ export function uninstallDependency(cwd: URI, fs: LocalFS, logger: Log.Logger, publisherId: string, libraryName: string): Promise<void>;
1942
+ /**
1943
+ * Install npm dependencies in pov and blo working paths if they exist and contain a package.json.
1944
+ * This ensures that a single `studio install` command sets up the entire project.
1945
+ * @param cwd
1946
+ * @param fs
1947
+ * @param logger
1948
+ */
1949
+ export function installNpmInApmParts(cwd: URI, fs: LocalFS, logger: Log.Logger): Promise<void>;
1938
1950
  export interface APMPartLibrary {
1939
1951
  name: string;
1940
1952
  path: URI;
@@ -1946,6 +1958,25 @@ export declare namespace Dependencies {
1946
1958
  * @param apmPart currently only dlo and dfiles are supported
1947
1959
  */
1948
1960
  export function getApmPartDependencies(cwd: URI, fs: LocalFS, apmPart: APM.Part.dlo | APM.Part.dfiles): Promise<APMPartLibrary[]>;
1961
+ /**
1962
+ * Link a library's blo/pov exports into the consuming project's part package.json files.
1963
+ * After a library is extracted, check if it exports blo or pov parts and add `file:` references.
1964
+ * @param cwd
1965
+ * @param fs
1966
+ * @param logger
1967
+ * @param libraryName - short name, e.g. "pets-demo-lib"
1968
+ * @param development
1969
+ */
1970
+ export function linkLibraryApmParts(cwd: URI, fs: LocalFS, logger: Log.Logger, libraryName: string, development?: boolean): Promise<void>;
1971
+ /**
1972
+ * Unlink a library's blo/pov exports from the consuming project's part package.json files.
1973
+ * Removes `file:` references that were added by linkLibraryApmParts.
1974
+ * @param cwd
1975
+ * @param fs
1976
+ * @param logger
1977
+ * @param libraryName - short name, e.g. "pets-demo-lib"
1978
+ */
1979
+ export function unlinkLibraryApmParts(cwd: URI, fs: LocalFS, logger: Log.Logger, libraryName: string): Promise<void>;
1949
1980
  }
1950
1981
 
1951
1982
  /**
@@ -2372,15 +2403,18 @@ export declare enum HttpMethod {
2372
2403
 
2373
2404
  /**
2374
2405
  * Installs the specified dependencies or all dependencies if none are provided.
2406
+ * Additionally runs `npm install` in pov and blo working paths if they exist and contain a package.json,
2407
+ * ensuring a single install command sets up the entire project.
2375
2408
  *
2376
2409
  * @param {URI} cwd - The current working directory where the installation process takes place.
2377
2410
  * @param {LocalFS} fs - The file system interface used for managing files during the installation process.
2411
+ * @param {Log.Logger} logger - Logger instance for logging details during the install process.
2378
2412
  * @param {Globals.ENV} env - The global environment configuration used during the installation.
2379
2413
  * @param {string|null} apiToken - The API token for authentication, if required, or null if not applicable.
2380
2414
  * @param {string[]} dependencies - An array of dependency strings in the format "libraryId@version". If empty, all dependencies will be installed.
2381
2415
  * @return {Promise<void>} A promise that resolves when the installation process completes successfully or logs an error if it fails.
2382
2416
  */
2383
- export declare function install(cwd: URI, fs: LocalFS, env: Globals.ENV, apiToken: string | null, dependencies: string[]): Promise<void>;
2417
+ export declare function install(cwd: URI, fs: LocalFS, logger: Log.Logger, env: Globals.ENV, apiToken: string | null, dependencies: string[]): Promise<void>;
2384
2418
 
2385
2419
  export declare namespace Library {
2386
2420
  /**
package/out/api.js CHANGED
@@ -41,24 +41,29 @@ const log_1 = require("./log");
41
41
  Object.defineProperty(exports, "Log", { enumerable: true, get: function () { return log_1.Log; } });
42
42
  /**
43
43
  * Installs the specified dependencies or all dependencies if none are provided.
44
+ * Additionally runs `npm install` in pov and blo working paths if they exist and contain a package.json,
45
+ * ensuring a single install command sets up the entire project.
44
46
  *
45
47
  * @param {URI} cwd - The current working directory where the installation process takes place.
46
48
  * @param {LocalFS} fs - The file system interface used for managing files during the installation process.
49
+ * @param {Log.Logger} logger - Logger instance for logging details during the install process.
47
50
  * @param {Globals.ENV} env - The global environment configuration used during the installation.
48
51
  * @param {string|null} apiToken - The API token for authentication, if required, or null if not applicable.
49
52
  * @param {string[]} dependencies - An array of dependency strings in the format "libraryId@version". If empty, all dependencies will be installed.
50
53
  * @return {Promise<void>} A promise that resolves when the installation process completes successfully or logs an error if it fails.
51
54
  */
52
- async function install(cwd, fs, env, apiToken, dependencies) {
55
+ async function install(cwd, fs, logger, env, apiToken, dependencies) {
53
56
  if (dependencies.length > 0) {
54
57
  for (const dependency of dependencies) {
55
58
  const [libraryId, version] = dependency.split('@');
56
- await dependencies_1.Dependencies.installDependency(cwd, fs, env, apiToken, libraryId, version || null);
59
+ await dependencies_1.Dependencies.installDependency(cwd, fs, logger, env, apiToken, libraryId, version || null);
57
60
  }
58
61
  }
59
62
  else {
60
- return await dependencies_1.Dependencies.installDependencies(cwd, fs, env, apiToken);
63
+ await dependencies_1.Dependencies.installDependencies(cwd, fs, logger, env, apiToken);
61
64
  }
65
+ // Run npm install in pov/blo working paths if they have a package.json
66
+ await dependencies_1.Dependencies.installNpmInApmParts(cwd, fs, logger);
62
67
  }
63
68
  /**
64
69
  * Builds the specified parts of the project located in the given directory.
package/out/dde/dde.js CHANGED
@@ -396,7 +396,7 @@ var DDE;
396
396
  const manifest = await manifest_1.Manifest.read(cwd, fs);
397
397
  const dependencies = await getDependencies(cwd, fs, manifest);
398
398
  const resolvedConfig = await globals_1.Globals.read(cwd, fs, logger);
399
- const autoInc = (0, dlo_1.generateAutoIncFromMap)(ddeJSON, resolvedConfig.flat, manifest);
399
+ const autoInc = (0, dlo_1.generateAutoIncFromMap)(ddeJSON, resolvedConfig, manifest);
400
400
  const appAutoDDE = new dlo_1.AutoDDEGenerator(manifest, ddeJSON, null);
401
401
  const appDDEInc = appAutoDDE.generate();
402
402
  // generate AutoDDe Files for the libraries:
@@ -97,7 +97,7 @@ function dloCoreSupportsFlags(manifest) {
97
97
  return semver.gte(releaseVersion, '1.1.0');
98
98
  }
99
99
  // eslint-disable-next-line sonarjs/cognitive-complexity
100
- function generateAutoIncFromMap(ddeMap, globalDefines, manifest) {
100
+ function generateAutoIncFromMap(ddeMap, resolvedConfig, manifest) {
101
101
  const _pwn = []; // sample code output for pawn
102
102
  const pwn = _pwn.push.bind(_pwn);
103
103
  function pwn_section(s) {
@@ -139,8 +139,24 @@ function generateAutoIncFromMap(ddeMap, globalDefines, manifest) {
139
139
  /**
140
140
  * generate global dlo defines if there are any
141
141
  */
142
+ const globalDefines = resolvedConfig.flat;
143
+ const { definesWithSource } = resolvedConfig;
142
144
  pwn_section('GLOBAL DEFINES');
143
145
  for (const [define, value] of Object.entries(globalDefines)) {
146
+ const comment = definesWithSource[define]?.comment;
147
+ if (comment) {
148
+ const lines = comment.split('\n');
149
+ if (lines.length === 1) {
150
+ pwn(`/** ${lines[0]} */`);
151
+ }
152
+ else {
153
+ pwn('/**');
154
+ for (const line of lines) {
155
+ pwn(` * ${line}`);
156
+ }
157
+ pwn(' */');
158
+ }
159
+ }
144
160
  const valueIsDefine = Object.hasOwn(globalDefines, value);
145
161
  addDefinition(define, `${value}`, valueIsDefine);
146
162
  }
@@ -30,9 +30,11 @@ const semver = __importStar(require("semver"));
30
30
  const registryAPI_1 = require("./registryAPI");
31
31
  const defaultFiles_1 = require("./defaultFiles");
32
32
  const apm_1 = require("./package/apm");
33
+ const child_process = __importStar(require("node:child_process"));
33
34
  const tinytar = require('tinytar-fix');
34
35
  const pako = require('pako');
35
36
  exports.LIB_DEPS_PATH = '.studio/libdeps';
37
+ const PACKAGE_JSON = 'package.json';
36
38
  var Dependencies;
37
39
  (function (Dependencies) {
38
40
  /**
@@ -163,11 +165,12 @@ var Dependencies;
163
165
  * @param cwd
164
166
  * @param fs
165
167
  * @param libraryName
168
+ * @param logger
166
169
  */
167
- async function removeExisting(cwd, fs, libraryName) {
170
+ async function removeExisting(cwd, fs, logger, libraryName) {
168
171
  const libraryPath = vscode_uri_1.Utils.joinPath(cwd, exports.LIB_DEPS_PATH, libraryName);
169
172
  await fs.rm(libraryPath).catch(err => {
170
- console.error('removeExisting', err);
173
+ logger.error('removeExisting', err);
171
174
  });
172
175
  }
173
176
  Dependencies.removeExisting = removeExisting;
@@ -175,19 +178,20 @@ var Dependencies;
175
178
  * Extract the given library.tar.gz array buffer
176
179
  * @param cwd
177
180
  * @param fs
181
+ * @param logger
178
182
  * @param libraryName
179
183
  * @param archiveBuffer
180
184
  */
181
- async function extractLibraryContent(cwd, fs, libraryName, archiveBuffer) {
185
+ async function extractLibraryContent(cwd, fs, logger, libraryName, archiveBuffer) {
182
186
  const files = tinytar.untar(pako.ungzip(archiveBuffer));
183
- await removeExisting(cwd, fs, libraryName);
187
+ await removeExisting(cwd, fs, logger, libraryName);
184
188
  for (const file of files.values()) {
185
189
  try {
186
190
  const filePath = vscode_uri_1.Utils.joinPath(cwd, exports.LIB_DEPS_PATH, libraryName, file.name);
187
191
  await fs.writeFile(filePath, file.data);
188
192
  }
189
193
  catch (e) {
190
- console.error('Extracting lib', e);
194
+ logger.error('Extracting lib', e);
191
195
  }
192
196
  }
193
197
  }
@@ -196,19 +200,20 @@ var Dependencies;
196
200
  * Install the given library with the version that the semver range accepts
197
201
  * @param cwd
198
202
  * @param fs
203
+ * @param logger
199
204
  * @param env
200
205
  * @param token
201
206
  * @param publisherIdName
202
207
  * @param version
203
208
  * @param development - install as development dependency
204
209
  */
205
- async function installDependency(cwd, fs, env, token, publisherIdName, version, development = false) {
210
+ async function installDependency(cwd, fs, logger, env, token, publisherIdName, version, development = false) {
206
211
  const [publisherId, libraryName] = publisherIdName.split('/');
207
212
  if (version && (await isDependencyInstalled(cwd, fs, publisherId, libraryName, version))) {
208
213
  return;
209
214
  }
210
215
  // just remove any existing version of this library.
211
- await removeExisting(cwd, fs, libraryName);
216
+ await removeExisting(cwd, fs, logger, libraryName);
212
217
  const libraryProfile = await registryAPI_1.Registry.getLibraryProfile(token, publisherId, libraryName, env);
213
218
  const versionToDownload = version
214
219
  ? libraryProfile.versions?.find(versionInfo => {
@@ -229,7 +234,7 @@ var Dependencies;
229
234
  await validateDependencyTree(cwd, fs, newDependencies, development);
230
235
  // install sub dependencies first
231
236
  for (const [libraryName, libraryVersion] of Object.entries(librarySubDependencies)) {
232
- await installDependency(cwd, fs, env, token, libraryName, libraryVersion, development);
237
+ await installDependency(cwd, fs, logger, env, token, libraryName, libraryVersion, development);
233
238
  }
234
239
  const libraryArchive = versionToDownload.files.find(file => {
235
240
  return file.name === 'library.tar.gz';
@@ -238,7 +243,8 @@ var Dependencies;
238
243
  throw new Error(`Library ${publisherIdName} has no content!`);
239
244
  }
240
245
  const libraryBuffer = await registryAPI_1.Registry.fetchCustomContent(token, libraryArchive.downloadUrl);
241
- await extractLibraryContent(cwd, fs, libraryName, libraryBuffer);
246
+ await extractLibraryContent(cwd, fs, logger, libraryName, libraryBuffer);
247
+ await linkLibraryApmParts(cwd, fs, logger, libraryName, development);
242
248
  // update manifest
243
249
  const manifest = await manifest_1.Manifest.read(cwd, fs);
244
250
  const dependencies = manifest.libraryDependencies || {};
@@ -255,10 +261,11 @@ var Dependencies;
255
261
  * Install all dependencies that are referenced within the studio manifest
256
262
  * @param cwd
257
263
  * @param fs
264
+ * @param logger
258
265
  * @param env
259
266
  * @param token
260
267
  */
261
- async function installDependencies(cwd, fs, env, token) {
268
+ async function installDependencies(cwd, fs, logger, env, token) {
262
269
  const manifest = await manifest_1.Manifest.read(cwd, fs);
263
270
  const dependencies = manifest.libraryDependencies || {};
264
271
  const devDependencies = manifest.libraryDevDependencies || {};
@@ -266,9 +273,16 @@ var Dependencies;
266
273
  for (const [libraryName, libraryVersion] of Object.entries(allDependencies)) {
267
274
  const isDevelopment = Object.hasOwn(devDependencies, libraryName);
268
275
  // update the tree version with the actual installed version
269
- await installDependency(cwd, fs, env, token, libraryName, libraryVersion, isDevelopment);
276
+ await installDependency(cwd, fs, logger, env, token, libraryName, libraryVersion, isDevelopment);
270
277
  }
271
- console.log(`Installed ${Object.entries(allDependencies).length} dependencies`);
278
+ // Ensure blo/pov file: references are linked for all dependencies
279
+ // (installDependency skips linking when the dependency is already installed)
280
+ for (const [libraryId] of Object.entries(allDependencies)) {
281
+ const libName = dependencyIdToName(libraryId);
282
+ const isDevelopment = Object.hasOwn(devDependencies, libraryId);
283
+ await linkLibraryApmParts(cwd, fs, logger, libName, isDevelopment);
284
+ }
285
+ logger.done(`Installed ${Object.entries(allDependencies).length} dependencies`);
272
286
  }
273
287
  Dependencies.installDependencies = installDependencies;
274
288
  /**
@@ -279,7 +293,7 @@ var Dependencies;
279
293
  * @param publisherId
280
294
  * @param libraryName
281
295
  */
282
- async function uninstallDependency(cwd, fs, publisherId, libraryName) {
296
+ async function uninstallDependency(cwd, fs, logger, publisherId, libraryName) {
283
297
  const manifest = await manifest_1.Manifest.read(cwd, fs);
284
298
  const dependencies = manifest.libraryDependencies || {};
285
299
  const devDependencies = manifest.libraryDevDependencies || {};
@@ -288,7 +302,8 @@ var Dependencies;
288
302
  delete allDependencies[libraryId];
289
303
  const validatedDependencies = await validateIfLibraryCanBeRemoved(cwd, fs, allDependencies);
290
304
  if (!Object.hasOwn(validatedDependencies, libraryId)) {
291
- await removeExisting(cwd, fs, libraryName);
305
+ await unlinkLibraryApmParts(cwd, fs, logger, libraryName);
306
+ await removeExisting(cwd, fs, logger, libraryName);
292
307
  delete devDependencies[libraryId];
293
308
  delete dependencies[libraryId];
294
309
  await manifest_1.Manifest.write(cwd, fs, manifest);
@@ -298,6 +313,33 @@ var Dependencies;
298
313
  }
299
314
  }
300
315
  Dependencies.uninstallDependency = uninstallDependency;
316
+ /**
317
+ * Install npm dependencies in pov and blo working paths if they exist and contain a package.json.
318
+ * This ensures that a single `studio install` command sets up the entire project.
319
+ * @param cwd
320
+ * @param fs
321
+ * @param logger
322
+ */
323
+ async function installNpmInApmParts(cwd, fs, logger) {
324
+ const manifest = await manifest_1.Manifest.read(cwd, fs);
325
+ const parts = [];
326
+ if (manifest.pov?.details?.workingPath) {
327
+ parts.push({ name: 'pov', workingPath: manifest.pov.details.workingPath });
328
+ }
329
+ if (manifest.blo?.workingPath) {
330
+ parts.push({ name: 'blo', workingPath: manifest.blo.workingPath });
331
+ }
332
+ for (const { name, workingPath } of parts) {
333
+ const partDir = vscode_uri_1.Utils.joinPath(cwd, workingPath);
334
+ const packageJsonPath = vscode_uri_1.Utils.joinPath(partDir, PACKAGE_JSON);
335
+ const hasPackageJson = await fs.stat(packageJsonPath);
336
+ if (hasPackageJson) {
337
+ logger.info(`Installing npm dependencies for ${name} (${workingPath})...`);
338
+ await execNpmInstall(partDir, logger);
339
+ }
340
+ }
341
+ }
342
+ Dependencies.installNpmInApmParts = installNpmInApmParts;
301
343
  /**
302
344
  * Get all libraries that have dlo source
303
345
  * @param cwd
@@ -336,7 +378,100 @@ var Dependencies;
336
378
  return apmDependencies;
337
379
  }
338
380
  Dependencies.getApmPartDependencies = getApmPartDependencies;
381
+ /**
382
+ * Link a library's blo/pov exports into the consuming project's part package.json files.
383
+ * After a library is extracted, check if it exports blo or pov parts and add `file:` references.
384
+ * @param cwd
385
+ * @param fs
386
+ * @param logger
387
+ * @param libraryName - short name, e.g. "pets-demo-lib"
388
+ * @param development
389
+ */
390
+ async function linkLibraryApmParts(cwd, fs, logger, libraryName, development) {
391
+ const manifest = await manifest_1.Manifest.read(cwd, fs);
392
+ const partConfigs = [
393
+ { partName: 'blo', partDistPath: 'blo', workingPath: manifest.blo?.workingPath },
394
+ { partName: 'pov', partDistPath: 'pov/details', workingPath: manifest.pov?.details?.workingPath }
395
+ ];
396
+ for (const { partName, partDistPath, workingPath } of partConfigs) {
397
+ if (!workingPath) {
398
+ continue;
399
+ }
400
+ const libPartPackageJson = vscode_uri_1.Utils.joinPath(cwd, exports.LIB_DEPS_PATH, libraryName, 'dist', ...partDistPath.split('/'), PACKAGE_JSON);
401
+ const libPartExists = await fs.stat(libPartPackageJson);
402
+ if (!libPartExists) {
403
+ continue;
404
+ }
405
+ try {
406
+ const pkg = await readPartPackageJson(cwd, fs, workingPath);
407
+ const depsKey = development ? 'devDependencies' : 'dependencies';
408
+ pkg[depsKey] = pkg[depsKey] || {};
409
+ const depth = workingPath.split('/').length;
410
+ const relativePrefix = Array(depth).fill('..').join('/');
411
+ pkg[depsKey][libraryName] = `file:${relativePrefix}/${exports.LIB_DEPS_PATH}/${libraryName}/dist/${partDistPath}`;
412
+ await writePartPackageJson(cwd, fs, workingPath, pkg);
413
+ logger.info(`Linked ${libraryName} to ${partName} (${workingPath})`);
414
+ }
415
+ catch (e) {
416
+ logger.warn(`Could not link ${libraryName} to ${partName}: ${e}`);
417
+ }
418
+ }
419
+ }
420
+ Dependencies.linkLibraryApmParts = linkLibraryApmParts;
421
+ /**
422
+ * Unlink a library's blo/pov exports from the consuming project's part package.json files.
423
+ * Removes `file:` references that were added by linkLibraryApmParts.
424
+ * @param cwd
425
+ * @param fs
426
+ * @param logger
427
+ * @param libraryName - short name, e.g. "pets-demo-lib"
428
+ */
429
+ async function unlinkLibraryApmParts(cwd, fs, logger, libraryName) {
430
+ const manifest = await manifest_1.Manifest.read(cwd, fs);
431
+ const partConfigs = [
432
+ { partName: 'blo', workingPath: manifest.blo?.workingPath },
433
+ { partName: 'pov', workingPath: manifest.pov?.details?.workingPath }
434
+ ];
435
+ for (const { partName, workingPath } of partConfigs) {
436
+ if (!workingPath) {
437
+ continue;
438
+ }
439
+ try {
440
+ const pkg = await readPartPackageJson(cwd, fs, workingPath);
441
+ let changed = false;
442
+ // Remove from dependencies if it's a file: reference
443
+ if (pkg.dependencies?.[libraryName]?.startsWith('file:')) {
444
+ delete pkg.dependencies[libraryName];
445
+ changed = true;
446
+ }
447
+ // Remove from devDependencies if it's a file: reference
448
+ if (pkg.devDependencies?.[libraryName]?.startsWith('file:')) {
449
+ delete pkg.devDependencies[libraryName];
450
+ changed = true;
451
+ }
452
+ if (changed) {
453
+ await writePartPackageJson(cwd, fs, workingPath, pkg);
454
+ logger.info(`Unlinked ${libraryName} from ${partName}`);
455
+ }
456
+ }
457
+ catch (e) {
458
+ logger.warn(`Could not unlink ${libraryName} from ${partName}: ${e}`);
459
+ }
460
+ }
461
+ }
462
+ Dependencies.unlinkLibraryApmParts = unlinkLibraryApmParts;
339
463
  })(Dependencies || (exports.Dependencies = Dependencies = {}));
464
+ async function readPartPackageJson(cwd, fs, workingPath) {
465
+ const packageJsonPath = vscode_uri_1.Utils.joinPath(cwd, workingPath, PACKAGE_JSON);
466
+ const data = await fs.readFile(packageJsonPath);
467
+ const text = new TextDecoder().decode(data);
468
+ return JSON.parse(text);
469
+ }
470
+ async function writePartPackageJson(cwd, fs, workingPath, pkg) {
471
+ const packageJsonPath = vscode_uri_1.Utils.joinPath(cwd, workingPath, PACKAGE_JSON);
472
+ const content = JSON.stringify(pkg, null, '\t') + '\n';
473
+ await fs.writeFile(packageJsonPath, content);
474
+ }
340
475
  async function fetchStudioJson(token, studioJsonURL) {
341
476
  return await registryAPI_1.Registry.fetchCustomContent(token, studioJsonURL, true);
342
477
  }
@@ -419,4 +554,25 @@ function buildDependencyEntry(publisherId, libraryId, libraryVersion) {
419
554
  libraryId: `${publisherId}/${libraryId}`
420
555
  };
421
556
  }
557
+ /**
558
+ * Execute `npm install` in the given directory
559
+ * @param cwd - working directory where npm install should be executed
560
+ * @param logger
561
+ */
562
+ function execNpmInstall(cwd, logger) {
563
+ return new Promise((resolve, reject) => {
564
+ child_process.exec('npm ci', { cwd: cwd.fsPath, env: process.env }, (error, stdout, stderr) => {
565
+ if (stdout) {
566
+ logger.log(stdout);
567
+ }
568
+ if (stderr) {
569
+ logger.error(stderr);
570
+ }
571
+ if (error) {
572
+ reject(error);
573
+ }
574
+ resolve();
575
+ });
576
+ });
577
+ }
422
578
  //# sourceMappingURL=dependencies.js.map
package/out/globals.js CHANGED
@@ -67,10 +67,10 @@ var Globals;
67
67
  const depPath = dependencies_1.Dependencies.dependencyToPath(cwd, dependencyId);
68
68
  const name = dependencies_1.Dependencies.dependencyIdToName(dependencyId);
69
69
  const libIniPath = vscode_uri_1.Utils.joinPath(depPath, DLO_PATH, `${name}.ini`);
70
- await mergeIniFile(fs, libIniPath, allDefines, sections, definesWithSource);
70
+ await mergeIniFile(fs, libIniPath, allDefines, sections, definesWithSource, name);
71
71
  // defaults.ini files
72
72
  const libDefaultsPath = vscode_uri_1.Utils.joinPath(depPath, defaultFiles_1.DefaultFiles.filePaths.defaultsIni);
73
- await mergeIniFile(fs, libDefaultsPath, allDefines, sections, definesWithSource);
73
+ await mergeIniFile(fs, libDefaultsPath, allDefines, sections, definesWithSource, name);
74
74
  }
75
75
  // Load the library project's specific ini file (deprecated location)
76
76
  if (manifest_1.Manifest.isLibrary(manifest)) {
@@ -78,7 +78,7 @@ var Globals;
78
78
  if (logger && (await fs.stat(libIni))) {
79
79
  logger.warn(`dlo/${manifest.name}.ini is deprecated. Please migrate your defines to defaults.ini in the project root.`);
80
80
  }
81
- await mergeIniFile(fs, libIni, allDefines, sections, definesWithSource);
81
+ await mergeIniFile(fs, libIni, allDefines, sections, definesWithSource, manifest.name);
82
82
  }
83
83
  // Load the project's main.ini and overwrite existing defines (deprecated location)
84
84
  const mainIniRelPath = manifest.dlo?.iniFile || defaultFiles_1.DefaultFiles.filePaths.dlo.mainINI;
@@ -86,17 +86,18 @@ var Globals;
86
86
  if (logger && (await fs.stat(mainIni))) {
87
87
  logger.warn(`${mainIniRelPath} is deprecated. Please migrate your defines to app.ini in the project root.`);
88
88
  }
89
- await mergeIniFile(fs, mainIni, allDefines, sections, definesWithSource);
89
+ const mainIniLabel = iniFileLabel(mainIniRelPath);
90
+ await mergeIniFile(fs, mainIni, allDefines, sections, definesWithSource, mainIniLabel);
90
91
  // Project root: defaults.ini (library) or app.ini (app/addon/analytics)
91
92
  if (manifest_1.Manifest.isLibrary(manifest)) {
92
93
  const defaultsIni = vscode_uri_1.Utils.joinPath(cwd, defaultFiles_1.DefaultFiles.filePaths.defaultsIni);
93
- await mergeIniFile(fs, defaultsIni, allDefines, sections, definesWithSource);
94
+ await mergeIniFile(fs, defaultsIni, allDefines, sections, definesWithSource, 'defaults');
94
95
  }
95
96
  // Snapshot defaults-only state before app.ini merge (for library dual-defines)
96
97
  const defaultsOnly = snapshotDefaultsOnly(manifest, allDefines, sections, definesWithSource);
97
98
  // app.ini is always loaded (provides local dev overrides for libraries, main config for apps)
98
99
  const appIni = vscode_uri_1.Utils.joinPath(cwd, defaultFiles_1.DefaultFiles.filePaths.appIni);
99
- await mergeIniFile(fs, appIni, allDefines, sections, definesWithSource);
100
+ await mergeIniFile(fs, appIni, allDefines, sections, definesWithSource, 'app');
100
101
  return { flat: allDefines, sections, definesWithSource, defaultsOnly };
101
102
  }
102
103
  Globals.read = read;
@@ -269,10 +270,35 @@ function formatJsDoc(comment) {
269
270
  function isNumericValue(value) {
270
271
  return value !== '' && !isNaN(Number(value));
271
272
  }
273
+ /**
274
+ * Derive a human-readable source label from an ini file path.
275
+ * Strips the directory prefix and `.ini` extension.
276
+ * E.g. `dlo/main.ini` → `main`, `app.ini` → `app`.
277
+ */
278
+ function iniFileLabel(relPath) {
279
+ const filename = relPath.split('/').pop() || relPath;
280
+ return filename.replace(/\.ini$/i, '');
281
+ }
282
+ /**
283
+ * Merge an incoming comment with an existing one.
284
+ *
285
+ * - Both present → join existing (already prefixed) and new (prefixed) with `\n`
286
+ * - Only existing → keep it
287
+ * - Only new → use it (prefixed)
288
+ * - Neither → undefined
289
+ */
290
+ function mergeComment(existingComment, newRawComment, sourceLabel) {
291
+ const prefixedNew = newRawComment ? `${sourceLabel}: ${newRawComment}` : undefined;
292
+ if (existingComment && prefixedNew) {
293
+ return `${existingComment}\n${prefixedNew}`;
294
+ }
295
+ return prefixedNew || existingComment;
296
+ }
272
297
  /**
273
298
  * Create a DefineEntry, attaching a comment from the comment map if available.
299
+ * The comment is prefixed with the source label for provenance tracking.
274
300
  */
275
- function createDefineEntry(stringValue, fileUri, lines, key, sectionName, commentMap) {
301
+ function createDefineEntry(stringValue, fileUri, lines, key, sectionName, commentMap, sourceLabel) {
276
302
  const flatKey = sectionName ? `${sectionName}_${key}` : key;
277
303
  const entry = {
278
304
  value: stringValue,
@@ -280,23 +306,26 @@ function createDefineEntry(stringValue, fileUri, lines, key, sectionName, commen
280
306
  sourceLine: findLineNumber(lines, key, sectionName)
281
307
  };
282
308
  if (commentMap[flatKey]) {
283
- entry.comment = commentMap[flatKey];
309
+ entry.comment = `${sourceLabel}: ${commentMap[flatKey]}`;
284
310
  }
285
311
  return entry;
286
312
  }
287
313
  /**
288
314
  * Merge all keys from a parsed ini section into the accumulators.
289
315
  */
290
- function mergeSectionEntries(sectionName, sectionData, allDefines, sections, definesWithSource, fileUri, lines, commentMap) {
316
+ function mergeSectionEntries(sectionName, sectionData, allDefines, sections, definesWithSource, fileUri, lines, commentMap, sourceLabel) {
291
317
  if (!sections[sectionName]) {
292
318
  sections[sectionName] = {};
293
319
  }
294
320
  for (const [key, sectionValue] of Object.entries(sectionData)) {
295
321
  const flatKey = `${sectionName}_${key}`;
322
+ const rawComment = commentMap[flatKey];
296
323
  const stringValue = `${sectionValue}`;
297
324
  allDefines[flatKey] = stringValue;
298
325
  sections[sectionName][key] = stringValue;
299
- definesWithSource[flatKey] = createDefineEntry(stringValue, fileUri, lines, key, sectionName, commentMap);
326
+ const newEntry = createDefineEntry(stringValue, fileUri, lines, key, sectionName, commentMap, sourceLabel);
327
+ newEntry.comment = mergeComment(definesWithSource[flatKey]?.comment, rawComment, sourceLabel);
328
+ definesWithSource[flatKey] = newEntry;
300
329
  }
301
330
  }
302
331
  /**
@@ -309,7 +338,7 @@ function mergeSectionEntries(sectionName, sectionData, allDefines, sections, def
309
338
  * @param sections - accumulated sectioned config (mutated)
310
339
  * @param definesWithSource - accumulated source tracking (mutated)
311
340
  */
312
- async function mergeIniFile(fs, filePath, allDefines, sections, definesWithSource) {
341
+ async function mergeIniFile(fs, filePath, allDefines, sections, definesWithSource, sourceLabel) {
313
342
  // early return if the file doesn't exist
314
343
  if (!(await fs.stat(filePath))) {
315
344
  return;
@@ -322,12 +351,15 @@ async function mergeIniFile(fs, filePath, allDefines, sections, definesWithSourc
322
351
  const commentMap = extractComments(rawText);
323
352
  for (const [sectionOrKey, value] of Object.entries(parsed)) {
324
353
  if (typeof value === 'object' && value !== null) {
325
- mergeSectionEntries(sectionOrKey, value, allDefines, sections, definesWithSource, fileUri, lines, commentMap);
354
+ mergeSectionEntries(sectionOrKey, value, allDefines, sections, definesWithSource, fileUri, lines, commentMap, sourceLabel);
326
355
  }
327
356
  else {
357
+ const rawComment = commentMap[sectionOrKey];
328
358
  const stringValue = `${value}`;
329
359
  allDefines[sectionOrKey] = stringValue;
330
- definesWithSource[sectionOrKey] = createDefineEntry(stringValue, fileUri, lines, sectionOrKey, null, commentMap);
360
+ const newEntry = createDefineEntry(stringValue, fileUri, lines, sectionOrKey, null, commentMap, sourceLabel);
361
+ newEntry.comment = mergeComment(definesWithSource[sectionOrKey]?.comment, rawComment, sourceLabel);
362
+ definesWithSource[sectionOrKey] = newEntry;
331
363
  }
332
364
  }
333
365
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microtronics/studio-cli",
3
- "version": "0.54.0",
3
+ "version": "0.56.0",
4
4
  "description": "Microtronics Studio CLI Tool",
5
5
  "main": "./out/api.js",
6
6
  "typings": "./dist/studio-cli.d.ts",