@jointhedots/gear 1.1.17 → 1.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/esm/builder/build-app-bundle.js +7 -6
  2. package/esm/builder/build-app-host.js +48 -6
  3. package/esm/builder/build-application.js +2 -2
  4. package/esm/builder/build-library.js +5 -3
  5. package/esm/builder/build-target.js +3 -3
  6. package/esm/builder/helpers/emit-bundle-manifest.js +1 -1
  7. package/esm/builder/helpers/emit-components-dts.js +66 -0
  8. package/esm/builder/helpers/emit-esmodules.js +1 -1
  9. package/esm/builder/helpers/emit-package-manifest.js +1 -1
  10. package/esm/builder/helpers/emit-static-assets.js +2 -2
  11. package/esm/builder/helpers/emit-typescript-definition.js +36 -17
  12. package/esm/cli.js +2 -0
  13. package/esm/commands/install.js +67 -0
  14. package/esm/commands/make.js +3 -3
  15. package/esm/commands/publish.js +1 -1
  16. package/esm/commands/serve.js +3 -3
  17. package/esm/publish/publish_aws_s3.js +2 -2
  18. package/esm/utils/normalized-name.js +11 -9
  19. package/esm/{model → workspace}/helpers/create-manifests.js +6 -0
  20. package/esm/{model → workspace}/helpers/discover-workspace.js +44 -61
  21. package/esm/workspace/packager.js +53 -0
  22. package/esm/workspace/packagers/packager-standard.js +39 -0
  23. package/package.json +7 -3
  24. /package/esm/{model → workspace}/component.js +0 -0
  25. /package/esm/{model → workspace}/helpers/config-loader.js +0 -0
  26. /package/esm/{model → workspace}/helpers/lockfile.js +0 -0
  27. /package/esm/{model → workspace}/helpers/logger.js +0 -0
  28. /package/esm/{model → workspace}/helpers/package-npm.js +0 -0
  29. /package/esm/{model → workspace}/storage.js +0 -0
  30. /package/esm/{model → workspace}/workspace.js +0 -0
@@ -1,11 +1,11 @@
1
1
  import Path from "node:path";
2
- import { Bundle, Library } from "../model/workspace.js";
3
- import { StorageFiles } from "../model/storage.js";
2
+ import { Bundle, Library } from "../workspace/workspace.js";
3
+ import { StorageFiles } from "../workspace/storage.js";
4
4
  import { BuildTarget } from "./build-target.js";
5
5
  import { TypescriptDefinitionTask } from "./helpers/emit-typescript-definition.js";
6
6
  import { PackageManifestTask } from "./helpers/emit-package-manifest.js";
7
7
  import { PathQualifier } from "./helpers/path-helpers.js";
8
- import { create_export_map } from "../model/helpers/create-manifests.js";
8
+ import { create_export_map } from "../workspace/helpers/create-manifests.js";
9
9
  import { DependencyDeduplicationPlugin, collectLibraryGraph } from "./helpers/emit-esmodules.js";
10
10
  import { BundleManifestTask } from "./helpers/emit-bundle-manifest.js";
11
11
  export var PathStatus;
@@ -120,14 +120,14 @@ export function create_bundle_target(opts) {
120
120
  }
121
121
  let state = paths_qualifier.check(path);
122
122
  if (state === PathStatus.Dependency || state === PathStatus.External) {
123
- //target.log.info(`External: ${path} <- ${importer}`)
123
+ //target.log.warn(`External: ${path} <- ${importer}`)
124
124
  return { external: true };
125
125
  }
126
126
  if (state === PathStatus.ExternalBundle) {
127
127
  throw new Error(`ExternalBundle: ${path} <- ${importer}`);
128
128
  }
129
129
  if (state instanceof Bundle) {
130
- //target.log.info(`Bundle: ${state.id} <- ${importer}`)
130
+ //target.log.warn(`Bundle: ${state.id} <- ${importer}`)
131
131
  const entry_id = state.resolve_export(path);
132
132
  if (!entry_id) {
133
133
  return { errors: [{ text: `Bundle '${state.id}' do not distribute expected entry: ${path}` }] };
@@ -148,7 +148,7 @@ export function create_bundle_target(opts) {
148
148
  namespace: "external-bundle-proxy",
149
149
  };
150
150
  }
151
- //target.log.trace(`Internal: ${path} <- ${importer}`)
151
+ //target.log.warn(`Internal: ${path} <- ${importer}`)
152
152
  });
153
153
  // Load virtual proxy modules for external bundle references
154
154
  build.onLoad({ filter: /.*/, namespace: "external-bundle-proxy" }, (args) => {
@@ -191,6 +191,7 @@ export async function build_app_composable_bundle(opts) {
191
191
  });
192
192
  target.log.info(`Build bundle: ${target.name}`);
193
193
  await target.build();
194
+ return target;
194
195
  }
195
196
  else {
196
197
  bundle.log.info(`Prebuild bundle: ${opts.bundle.id}`);
@@ -1,12 +1,50 @@
1
- import { Bundle, matchComponentSelection, Workspace, } from "../model/workspace.js";
2
- import { StorageFiles } from "../model/storage.js";
1
+ import { Bundle, matchComponentSelection, Workspace, } from "../workspace/workspace.js";
2
+ import { StorageFiles } from "../workspace/storage.js";
3
3
  import { BuildTarget } from "./build-target.js";
4
4
  import { build_app_composable_bundle } from "./build-app-bundle.js";
5
5
  import Path from "node:path";
6
6
  import { PWAPackageTask, WebviewTask } from "./build-application.js";
7
+ import { makeComponentPublication } from "../workspace/component.js";
7
8
  import { topologicalSort } from "../utils/graph-ordering.js";
9
+ import { BuildTask } from "./helpers/task.js";
10
+ export class ShelveManifestTask extends BuildTask {
11
+ target;
12
+ bundles;
13
+ constructor(target, bundles) {
14
+ super(target);
15
+ this.target = target;
16
+ this.bundles = bundles;
17
+ }
18
+ async execute() {
19
+ const { bundles } = this;
20
+ // Emit static components manifest
21
+ const namespaces = new Set();
22
+ const components = [];
23
+ for (const bundle of bundles) {
24
+ const pub = makeComponentPublication(bundle.manifest);
25
+ const nss = bundle.manifest.type === "bundle" && bundle.manifest.data?.["namespaces"];
26
+ if (Array.isArray(nss)) {
27
+ for (const ns of nss) {
28
+ namespaces.add(ns);
29
+ }
30
+ }
31
+ components.push(pub);
32
+ }
33
+ // Emit bundle manifest
34
+ const tx = this.target.edit();
35
+ const manifest = {
36
+ $id: ".",
37
+ type: "bundle",
38
+ data: {
39
+ namespaces: Array.from(namespaces),
40
+ components,
41
+ }
42
+ };
43
+ await tx.commitFile(`bundle.manifest.json`, JSON.stringify(manifest, null, 2));
44
+ }
45
+ }
8
46
  function create_application_composable_target(opts) {
9
- const { app, version } = opts;
47
+ const { app, bundles, version } = opts;
10
48
  const { type, name, webviews, modules, assets, components } = app.descriptor;
11
49
  const lib = app.library;
12
50
  const bundle = lib.bundle;
@@ -24,6 +62,8 @@ function create_application_composable_target(opts) {
24
62
  html_injects.push(`<link rel="manifest" href="/manifest.json">`);
25
63
  html_injects.push(`<link rel="icon" type="image/webp" href="favicon.webp" />`);
26
64
  target.tasks.push(new PWAPackageTask(target, app));
65
+ // Generate shelve manifest
66
+ target.tasks.push(new ShelveManifestTask(target, bundles));
27
67
  // Add application webviews
28
68
  for (const name in webviews) {
29
69
  const { title, favicon } = webviews[name];
@@ -103,9 +143,9 @@ export async function build_app_composable_host(opts) {
103
143
  if (shelveBundles.missing.length > 0) {
104
144
  ws.log.warn(`Missing bundle dependencies: ${shelveBundles.missing.join(", ")}`);
105
145
  }
106
- await Promise.all(shelvePendings);
107
146
  const target = create_application_composable_target({
108
147
  app,
148
+ bundles: await Promise.all(shelveBundles),
109
149
  storage: opts.storage,
110
150
  version: opts.version,
111
151
  devmode: opts.devmode,
@@ -139,8 +179,10 @@ export class BundleSelector {
139
179
  this.selected.set(bun.id, bun);
140
180
  // Recursively add dependencies
141
181
  const deps = bun.dependencies;
142
- for (const depId of deps) {
143
- this.add(depId);
182
+ if (deps) {
183
+ for (const depId of deps) {
184
+ this.add(depId);
185
+ }
144
186
  }
145
187
  if (bun.source) {
146
188
  for (const depId in bun.source.descriptor?.dependencies) {
@@ -1,8 +1,8 @@
1
1
  import Path from "node:path";
2
2
  import Sharp from "sharp";
3
3
  import MIME from 'mime';
4
- import { Library, matchComponentSelection } from "../model/workspace.js";
5
- import { StorageFiles } from "../model/storage.js";
4
+ import { Library, matchComponentSelection } from "../workspace/workspace.js";
5
+ import { StorageFiles } from "../workspace/storage.js";
6
6
  import { BuildTarget } from "./build-target.js";
7
7
  import { build_app_composable_host } from "./build-app-host.js";
8
8
  import { DependencyDeduplicationPlugin } from "./helpers/emit-esmodules.js";
@@ -1,12 +1,13 @@
1
1
  import Path from "node:path";
2
2
  import ChildProcess from "child_process";
3
- import { Library } from "../model/workspace.js";
4
- import { StorageFiles } from "../model/storage.js";
3
+ import { Library } from "../workspace/workspace.js";
4
+ import { StorageFiles } from "../workspace/storage.js";
5
5
  import { BuildTarget } from "./build-target.js";
6
6
  import { TypescriptDefinitionTask } from "./helpers/emit-typescript-definition.js";
7
7
  import { PackageManifestTask } from "./helpers/emit-package-manifest.js";
8
- import { create_export_map } from "../model/helpers/create-manifests.js";
8
+ import { create_export_map } from "../workspace/helpers/create-manifests.js";
9
9
  import { BundleManifestTask } from "./helpers/emit-bundle-manifest.js";
10
+ import { ComponentsDtsTask } from "./helpers/emit-components-dts.js";
10
11
  export function create_library_target(opts) {
11
12
  const lib = opts.library;
12
13
  const target = new BuildTarget(lib.name, opts.storage, lib.workspace, opts.devmode == true, opts.watch == true, opts.clean == true);
@@ -22,6 +23,7 @@ export function create_library_target(opts) {
22
23
  target.tasks.push(new TypescriptDefinitionTask(target, lib));
23
24
  // Add library package.json
24
25
  target.tasks.push(new PackageManifestTask(target, lib, opts.version));
26
+ target.tasks.push(new ComponentsDtsTask(target, lib.bundle));
25
27
  // Add bundle content
26
28
  const { bundle } = lib;
27
29
  if (bundle) {
@@ -1,7 +1,7 @@
1
- import {} from "../model/component.js";
2
- import { Library, Workspace } from "../model/workspace.js";
1
+ import {} from "../workspace/component.js";
2
+ import { Library, Workspace } from "../workspace/workspace.js";
3
3
  import { ESModulesTask } from "./helpers/emit-esmodules.js";
4
- import {} from "../model/storage.js";
4
+ import {} from "../workspace/storage.js";
5
5
  import { AssetsTask } from "./helpers/emit-static-assets.js";
6
6
  export class BuildTarget {
7
7
  name;
@@ -1,4 +1,4 @@
1
- import { makeComponentPublication } from "../../model/component.js";
1
+ import { makeComponentPublication } from "../../workspace/component.js";
2
2
  import { BuildTarget } from "../build-target.js";
3
3
  import { BuildTask } from "./task.js";
4
4
  import MIME from 'mime';
@@ -0,0 +1,66 @@
1
+ import { join } from "node:path";
2
+ import { makeComponentPublication } from "../../workspace/component.js";
3
+ import { BuildTarget } from "../build-target.js";
4
+ import { BuildTask } from "./task.js";
5
+ import MIME from 'mime';
6
+ export class ComponentsDtsTask extends BuildTask {
7
+ target;
8
+ bundle;
9
+ constructor(target, bundle) {
10
+ super(target);
11
+ this.target = target;
12
+ this.bundle = bundle;
13
+ }
14
+ async execute() {
15
+ const { target, bundle } = this;
16
+ const tx = this.target.edit();
17
+ const chunks = [];
18
+ // Emit static components manifest
19
+ for (const manif of target.components.values()) {
20
+ chunks.push("\n");
21
+ generate_component_apis(manif, chunks);
22
+ chunks.push("\n");
23
+ }
24
+ // Emit bundle manifest
25
+ const dts = chunks.join("");
26
+ await tx.commitFile(`components.d.ts`, dts);
27
+ //console.log(dts)
28
+ }
29
+ }
30
+ function generate_component_apis(manif, chunks) {
31
+ for (const service in manif.apis) {
32
+ const generator = components_dts_generators[service];
33
+ if (generator) {
34
+ generator(manif, chunks);
35
+ }
36
+ }
37
+ }
38
+ function generate_view_react(manif, chunks) {
39
+ const props = manif.specs?.view?.properties;
40
+ chunks.push(`declare module 'view.react:${manif.$id}' {\n`);
41
+ chunks.push(` import type { ReactComponentType, ReactComponentDescriptor } from "@jointhedots/core/interfaces/react"\n`);
42
+ if (props && Object.keys(props).length > 0) {
43
+ chunks.push(` export type Props = {\n`);
44
+ for (const key of Object.keys(props)) {
45
+ chunks.push(` ${key}?: any\n`);
46
+ }
47
+ chunks.push(` }\n`);
48
+ chunks.push(` export type Component = ReactComponentType<Props>\n`);
49
+ }
50
+ else {
51
+ chunks.push(` export type Component = ReactComponentType\n`);
52
+ }
53
+ chunks.push(` export type Descriptor = ReactComponentDescriptor\n`);
54
+ chunks.push(`}\n`);
55
+ }
56
+ function generate_commands(manif, chunks) {
57
+ chunks.push(`declare module 'commands:${manif.$id}' {\n`);
58
+ chunks.push(` import type { CommandsService, Cmdlet } from "@jointhedots/core/interfaces/commands"\n`);
59
+ chunks.push(` export type Service = CommandsService\n`);
60
+ chunks.push(`}\n`);
61
+ }
62
+ const components_dts_generators = {
63
+ "view.react": generate_view_react,
64
+ "commands": generate_commands,
65
+ };
66
+ //# sourceMappingURL=emit-components-dts.js.map
@@ -6,7 +6,7 @@ import postcss from 'postcss';
6
6
  import * as esbuild from 'esbuild';
7
7
  import { sassPlugin } from 'esbuild-sass-plugin';
8
8
  import { PackageRootDir, resolve_normalized_suffixed_path } from "../../utils/file.js";
9
- import { Library } from "../../model/workspace.js";
9
+ import { Library } from "../../workspace/workspace.js";
10
10
  import { computeNameHashID } from "../../utils/normalized-name.js";
11
11
  import { BuildTask } from "./task.js";
12
12
  const VirtualOutDir = process.platform === 'win32' ? 'X:\\' : '/_/';
@@ -1,5 +1,5 @@
1
1
  import { BuildTask } from "./task.js";
2
- import { create_export_map, create_package_manifest } from "../../model/helpers/create-manifests.js";
2
+ import { create_export_map, create_package_manifest } from "../../workspace/helpers/create-manifests.js";
3
3
  import { console } from "node:inspector";
4
4
  export class PackageManifestTask extends BuildTask {
5
5
  library;
@@ -1,7 +1,7 @@
1
1
  import Path from "node:path";
2
2
  import MIME from 'mime';
3
- import { Library } from "../../model/workspace.js";
4
- import { copyToStorageStream } from "../../model/storage.js";
3
+ import { Library } from "../../workspace/workspace.js";
4
+ import { copyToStorageStream } from "../../workspace/storage.js";
5
5
  import { BuildTask } from "./task.js";
6
6
  export class AssetsTask extends BuildTask {
7
7
  assets = [];
@@ -6,12 +6,16 @@ import { execFile } from 'child_process';
6
6
  import { promisify } from 'util';
7
7
  import { BuildTask } from "./task.js";
8
8
  import { BuildTarget } from "../build-target.js";
9
- import { Library } from "../../model/workspace.js";
9
+ import { Library } from "../../workspace/workspace.js";
10
10
  import { file } from "../../utils/file.js";
11
- import { create_export_map } from "../../model/helpers/create-manifests.js";
11
+ import { create_export_map } from "../../workspace/helpers/create-manifests.js";
12
12
  const eol = Os.EOL;
13
13
  const indent = " ";
14
14
  const DTSLEN = '.d.ts'.length;
15
+ const EXT_RE = /\.(d\.ts|tsx?|jsx?|mjs|json)$/;
16
+ function normalizeModuleId(id) {
17
+ return id.replace(EXT_RE, '').replace(/\/index$/, '');
18
+ }
15
19
  function normalizeFileName(filename) {
16
20
  return filename.replaceAll(Path.sep, "/");
17
21
  }
@@ -75,6 +79,19 @@ function isNodeKindStringLiteral(value) {
75
79
  function isNodeKindExportDeclaration(value) {
76
80
  return value && value.kind === Ts.SyntaxKind.ExportDeclaration;
77
81
  }
82
+ function hasDefaultExport(sourceFile) {
83
+ return sourceFile.statements.some(stmt => {
84
+ if (stmt.kind === Ts.SyntaxKind.ExportAssignment)
85
+ return !stmt.isExportEquals;
86
+ const mods = Ts.canHaveModifiers(stmt) ? Ts.getModifiers(stmt) : undefined;
87
+ if (mods?.some(m => m.kind === Ts.SyntaxKind.ExportKeyword) && mods?.some(m => m.kind === Ts.SyntaxKind.DefaultKeyword))
88
+ return true;
89
+ if (isNodeKindExportDeclaration(stmt) && stmt.exportClause && Ts.isNamedExports(stmt.exportClause)) {
90
+ return stmt.exportClause.elements.some(e => e.name.text === 'default');
91
+ }
92
+ return false;
93
+ });
94
+ }
78
95
  class TypescriptProject {
79
96
  baseDir;
80
97
  compilerOptions;
@@ -147,6 +164,7 @@ export function generateTypescriptDefinition(options) {
147
164
  const normalizedBaseDir = normalizeFileName(Path.resolve(baseDir)) + "/";
148
165
  const normalizedDtsDir = normalizeFileName(Path.resolve(project.dtsDir)) + "/";
149
166
  const resolveCache = new Map();
167
+ const modulesWithDefault = new Set();
150
168
  const outputParts = [];
151
169
  if (options.externs) {
152
170
  options.externs.forEach(function (path) {
@@ -173,17 +191,22 @@ export function generateTypescriptDefinition(options) {
173
191
  // Emit public re-export modules
174
192
  if (options.exports) {
175
193
  for (const entry of Object.values(options.exports)) {
194
+ if (!entry.source)
195
+ continue;
176
196
  const internalId = resolveInternalModuleImport(entry.source);
177
- if (internalId) {
197
+ if (internalId.startsWith(prefix)) {
178
198
  outputParts.push(`declare module '${entry.id}' {${eol}`);
179
199
  outputParts.push(`${indent}export * from '${internalId}';${eol}`);
200
+ if (modulesWithDefault.has(internalId)) {
201
+ outputParts.push(`${indent}export { default } from '${internalId}';${eol}`);
202
+ }
180
203
  outputParts.push(`}${eol}${eol}`);
181
204
  }
182
205
  }
183
206
  }
184
207
  function resolveInternalModuleImport(moduleId, importDir = "") {
185
208
  if (moduleId.charAt(0) === '.') {
186
- return prefix + normalizeFileName(Path.join(importDir, moduleId));
209
+ return prefix + normalizeModuleId(normalizeFileName(Path.join(importDir, moduleId)));
187
210
  }
188
211
  if (resolveCache.has(moduleId))
189
212
  return resolveCache.get(moduleId);
@@ -192,29 +215,25 @@ export function generateTypescriptDefinition(options) {
192
215
  if (result.resolvedModule && !result.resolvedModule.isExternalLibraryImport) {
193
216
  const resolved = normalizeFileName(result.resolvedModule.resolvedFileName);
194
217
  if (resolved.startsWith(normalizedBaseDir)) {
195
- const value = prefix + resolved.slice(normalizedBaseDir.length).replace(/\.(d\.ts|tsx?|jsx?)$/, '');
218
+ const value = prefix + normalizeModuleId(resolved.slice(normalizedBaseDir.length));
196
219
  resolveCache.set(moduleId, value);
197
220
  return value;
198
221
  }
199
222
  }
200
- // Check if moduleId is already an internal path relative to baseDir
201
- const absolute = normalizeFileName(Path.resolve(baseDir, importDir, moduleId));
202
- if (absolute.startsWith(normalizedBaseDir)) {
203
- const value = prefix + absolute.slice(normalizedBaseDir.length);
204
- resolveCache.set(moduleId, value);
205
- return value;
206
- }
207
- resolveCache.set(moduleId, null);
208
- return null;
223
+ // Not resolved as internal keep the original module specifier
224
+ resolveCache.set(moduleId, moduleId);
225
+ return moduleId;
209
226
  }
210
227
  function writeDeclaration(declarationFile, rawSourceModuleId) {
211
- const moduleDeclId = resolveInternalModuleImport(rawSourceModuleId);
228
+ const moduleDeclId = prefix + normalizeModuleId(rawSourceModuleId);
212
229
  const moduleDir = Path.dirname(rawSourceModuleId);
230
+ if (hasDefaultExport(declarationFile))
231
+ modulesWithDefault.add(moduleDeclId);
213
232
  outputParts.push(`declare module '${moduleDeclId}' {${eol}${indent}`);
214
233
  const content = processTree(declarationFile, function (node) {
215
234
  if (isNodeKindExternalModuleReference(node)) {
216
235
  const expression = node.expression;
217
- const resolved = resolveInternalModuleImport(expression.text, moduleDir) ?? expression.text;
236
+ const resolved = resolveInternalModuleImport(expression.text, moduleDir);
218
237
  return ` require('${resolved}')`;
219
238
  }
220
239
  else if (isNodeKindImportDeclaration(node) && !node.importClause) {
@@ -226,7 +245,7 @@ export function generateTypescriptDefinition(options) {
226
245
  else if (isNodeKindStringLiteral(node) && node.parent &&
227
246
  (isNodeKindExportDeclaration(node.parent) || isNodeKindImportDeclaration(node.parent) || isNodeKindImportType(node.parent?.parent))) {
228
247
  const resolved = resolveInternalModuleImport(node.text, moduleDir);
229
- if (resolved != null) {
248
+ if (resolved !== node.text) {
230
249
  return ` '${resolved}'`;
231
250
  }
232
251
  }
package/esm/cli.js CHANGED
@@ -4,6 +4,7 @@ import Yargs from "yargs";
4
4
  import { hideBin } from 'yargs/helpers';
5
5
  import { command_run } from "./commands/run.js";
6
6
  import { command_init } from "./commands/init.js";
7
+ import { command_install } from "./commands/install.js";
7
8
  import { command_make } from "./commands/make.js";
8
9
  import { command_serve } from "./commands/serve.js";
9
10
  import { command_publish } from "./commands/publish.js";
@@ -17,6 +18,7 @@ function command_fail() {
17
18
  }
18
19
  Yargs(hideBin(Process.argv)).scriptName("jointhedots-gear")
19
20
  .command(command_init())
21
+ .command(command_install())
20
22
  .command(command_make())
21
23
  .command(command_serve())
22
24
  .command(command_publish())
@@ -0,0 +1,67 @@
1
+ import Path from 'node:path';
2
+ import Fs from 'node:fs';
3
+ import {} from "yargs";
4
+ import { open_workspace } from "../workspace/workspace.js";
5
+ import { makeComponentPublication } from "../workspace/component.js";
6
+ import { BundleSelector } from "../builder/build-app-host.js";
7
+ export function command_install() {
8
+ return {
9
+ command: 'install',
10
+ describe: 'Install workspace bundles and emit a shelve manifest',
11
+ builder: (yargs) => yargs
12
+ .strict()
13
+ .option("ws", {
14
+ type: "string",
15
+ default: ".",
16
+ describe: "Workspace directory"
17
+ })
18
+ .option("dist", {
19
+ type: "string",
20
+ default: ".",
21
+ describe: "Output directory"
22
+ }),
23
+ handler: async (argv) => {
24
+ const ws = await open_workspace({
25
+ workspace_path: argv.ws,
26
+ devmode: false,
27
+ ignored_directory: Path.resolve(argv.dist),
28
+ });
29
+ // Select all workspace bundles
30
+ const selector = new BundleSelector(ws);
31
+ for (const bundle of ws.bundles) {
32
+ selector.add(bundle);
33
+ }
34
+ if (selector.missing.length > 0) {
35
+ ws.log.warn(`Missing bundle dependencies: ${selector.missing.join(", ")}`);
36
+ }
37
+ // Build shelve manifest indexing all bundles
38
+ const namespaces = new Set();
39
+ const components = [];
40
+ for (const bundle of selector) {
41
+ const pub = makeComponentPublication(bundle.manifest);
42
+ const nss = bundle.manifest.type === "bundle" && bundle.manifest.data?.["namespaces"];
43
+ if (Array.isArray(nss)) {
44
+ for (const ns of nss) {
45
+ namespaces.add(ns);
46
+ }
47
+ }
48
+ components.push(pub);
49
+ }
50
+ const manifest = {
51
+ $id: ".",
52
+ type: "bundle",
53
+ data: {
54
+ namespaces: Array.from(namespaces),
55
+ components,
56
+ }
57
+ };
58
+ // Write bundle.manifest.json to dist
59
+ const distDir = Path.resolve(argv.dist);
60
+ Fs.mkdirSync(distDir, { recursive: true });
61
+ const manifestPath = Path.join(distDir, "bundle.manifest.json");
62
+ Fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
63
+ ws.log.success(`Shelve manifest written to ${manifestPath}`);
64
+ }
65
+ };
66
+ }
67
+ //# sourceMappingURL=install.js.map
@@ -1,12 +1,12 @@
1
1
  import Path from 'node:path';
2
2
  import {} from "yargs";
3
- import { StorageFiles } from "../model/storage.js";
4
- import { Bundle, Library, open_workspace } from "../model/workspace.js";
3
+ import { StorageFiles } from "../workspace/storage.js";
4
+ import { Bundle, Library, open_workspace } from "../workspace/workspace.js";
5
5
  import { build_application } from "../builder/build-application.js";
6
6
  import { build_app_composable_bundle } from "../builder/build-app-bundle.js";
7
7
  import { build_library } from "../builder/build-library.js";
8
8
  import { makeNormalizedName, NameStyle } from '../utils/normalized-name.js';
9
- import { resolvePackageVersion } from "../model/helpers/package-npm.js";
9
+ import { resolvePackageVersion } from "../workspace/helpers/package-npm.js";
10
10
  export function command_make() {
11
11
  return {
12
12
  command: 'make',
@@ -1,7 +1,7 @@
1
1
  import Path from 'node:path';
2
2
  import {} from "yargs";
3
3
  import { publish_aws_s3 } from '../publish/publish_aws_s3.js';
4
- import { open_workspace } from "../model/workspace.js";
4
+ import { open_workspace } from "../workspace/workspace.js";
5
5
  export function command_publish() {
6
6
  return {
7
7
  command: 'publish',
@@ -1,11 +1,11 @@
1
1
  import Path from 'node:path';
2
2
  import {} from "yargs";
3
- import { StorageFiles } from "../model/storage.js";
4
- import { open_workspace, Workspace } from "../model/workspace.js";
3
+ import { StorageFiles } from "../workspace/storage.js";
4
+ import { open_workspace, Workspace } from "../workspace/workspace.js";
5
5
  import { build_application } from "../builder/build-application.js";
6
6
  import { makeNormalizedName, NameStyle } from '../utils/normalized-name.js';
7
7
  import Express from 'express';
8
- import { resolvePackageVersion } from "../model/helpers/package-npm.js";
8
+ import { resolvePackageVersion } from "../workspace/helpers/package-npm.js";
9
9
  export function command_serve() {
10
10
  return {
11
11
  command: 'serve',
@@ -2,9 +2,9 @@ import Fs from "node:fs";
2
2
  import Path from "node:path";
3
3
  import * as AWS from "@aws-sdk/client-s3";
4
4
  import MIME from "mime";
5
- import { StorageFiles } from "../model/storage.js";
5
+ import { StorageFiles } from "../workspace/storage.js";
6
6
  import { build_application } from "../builder/build-application.js";
7
- import { Workspace } from "../model/workspace.js";
7
+ import { Workspace } from "../workspace/workspace.js";
8
8
  export async function publish_aws_s3(appname, ws, bucket, region, outputDir) {
9
9
  const storage = new StorageFiles(ws.name, outputDir);
10
10
  const app = ws.get_application(appname);
@@ -50,15 +50,9 @@ export function isNormalizedName(name, expectedStyle) {
50
50
  const style = expectedStyle ?? detectNormalizedNameStyle(name);
51
51
  return check_name_regexes[style].test(name);
52
52
  }
53
- export function makeNormalizedKey(key, targetStyle) {
54
- const seps = NormalizedNameSeparators[targetStyle];
55
- const parts = key.replace(/[^a-z0-9\s\t\n:._-]/g, "").split(/[\s\t\n:._-]/g);
56
- return parts.filter(x => !!x).join(seps.key);
57
- }
58
- export function makeNormalizedName(name, targetStyle, fromStyle) {
53
+ export function getNormalizedKeys(name, fromStyle) {
59
54
  if (!name || typeof name !== "string")
60
- return name;
61
- const seps = NormalizedNameSeparators[targetStyle];
55
+ return [];
62
56
  // Split into lowercase keys
63
57
  let keys;
64
58
  name = name.toLowerCase();
@@ -81,8 +75,16 @@ export function makeNormalizedName(name, targetStyle, fromStyle) {
81
75
  }
82
76
  // Normalize keys
83
77
  for (let i = 0; i < keys.length; i++) {
84
- keys[i] = makeNormalizedKey(keys[i], targetStyle);
78
+ keys[i] = keys[i].replace(/[^a-z0-9\s\t\n:._-]/g, "");
85
79
  }
80
+ return keys;
81
+ }
82
+ export function makeNormalizedName(name, targetStyle, fromStyle) {
83
+ if (!name || typeof name !== "string")
84
+ return name;
85
+ const seps = NormalizedNameSeparators[targetStyle];
86
+ // Split into lowercase keys
87
+ const keys = getNormalizedKeys(name, fromStyle);
86
88
  // Reform name, filtering out empty keys
87
89
  return keys.filter(x => !!x).join(seps.namespace);
88
90
  }
@@ -1,6 +1,7 @@
1
1
  import Path from "node:path";
2
2
  import { makeComponentPublication } from "../component.js";
3
3
  import { resolve_normalized_suffixed_path } from "../../utils/file.js";
4
+ import { getNormalizedKeys } from "../../utils/normalized-name.js";
4
5
  function add_export_entry(exports, lib, key, value, baseDir) {
5
6
  const basename = key.startsWith("./") ? key.slice(2) : key;
6
7
  const filename = lib.make_file_id("export", basename);
@@ -49,13 +50,18 @@ export function create_bundle_manifest(lib, bun) {
49
50
  return null;
50
51
  const entries = create_export_map(lib, bun);
51
52
  const bundle_manif = bun.manifest;
53
+ const namespaces = new Set();
52
54
  const components = [];
53
55
  for (const comp of bun.components.values()) {
56
+ const ns = getNormalizedKeys(comp.$id)[0];
57
+ if (ns)
58
+ namespaces.add(ns);
54
59
  components.push(makeComponentPublication(comp));
55
60
  }
56
61
  bundle_manif.data = {
57
62
  baseline: bun.id + "-v0",
58
63
  components: components,
64
+ namespaces: Array.from(namespaces),
59
65
  exports: {},
60
66
  };
61
67
  for (const id in entries) {
@@ -8,6 +8,8 @@ import { Bundle, Library, Workspace } from "../workspace.js";
8
8
  import { file, make_canonical_path, make_normalized_dirname, make_normalized_path, make_relative_path } from "../../utils/file.js";
9
9
  import { findConfigFile, is_config_filename, readConfigFile, readSingletonConfigFile } from "./config-loader.js";
10
10
  import { read_lockfile } from "./lockfile.js";
11
+ import { LoadLibraryPackager } from "../packager.js";
12
+ import { BundledLibraryPackager, DefaultLibraryPackager } from "../packagers/packager-standard.js";
11
13
  const exclude_dirs = ["node_modules", ".git"];
12
14
  function is_ignored_dir(ws, path) {
13
15
  const normalized_path = make_normalized_path(path);
@@ -18,9 +20,9 @@ function is_ignored_dir(ws, path) {
18
20
  }
19
21
  return false;
20
22
  }
21
- function setup_library_bundle(lib, bundle_desc) {
23
+ export function setup_library_bundle(lib) {
22
24
  const ws = lib.workspace;
23
- const manif = make_library_bundle_manifest(lib, bundle_desc);
25
+ const manif = make_library_bundle_manifest(lib);
24
26
  let bun = ws.get_bundle(manif.$id);
25
27
  if (!bun) {
26
28
  bun = new Bundle(manif, lib.path, ws, lib);
@@ -33,7 +35,7 @@ function setup_library_bundle(lib, bundle_desc) {
33
35
  throw new Error(`Library '${lib.get_id()}' is associate to a bundle '${bun.id}' that is already associated`);
34
36
  }
35
37
  }
36
- async function discover_component(lib, fpath) {
38
+ export async function discover_component(lib, fpath) {
37
39
  let bundle = lib.bundle;
38
40
  if (!bundle) {
39
41
  bundle = setup_library_bundle(lib);
@@ -90,7 +92,7 @@ async function discover_application(lib, fpath) {
90
92
  lib.log.error(`invalid application at ${fpath}: ${e?.message}`);
91
93
  }
92
94
  }
93
- async function discover_library_components(lib, path, subdir = false) {
95
+ export async function discover_library_components(lib, path, subdir = false) {
94
96
  // List names from directory
95
97
  const fnames = await Fsp.readdir(path);
96
98
  if (subdir && fnames.includes("package.json")) {
@@ -136,50 +138,25 @@ export function collect_declarations_field(lib, key, value) {
136
138
  }
137
139
  return value;
138
140
  }
139
- function make_library_bundle_manifest(lib, file_desc) {
140
- let $id = file_desc?.$id;
141
- if ($id) {
142
- $id = makeNormalizedName($id, NameStyle.OBJECT);
143
- if ($id !== file_desc.$id) {
144
- lib.log.warn(`Bundle '${file_desc.$id}' is normalized into '${$id}'`);
141
+ function make_library_bundle_manifest(lib) {
142
+ return {
143
+ $id: makeNormalizedName(collect_declarations_field(lib, "$id", lib.name), NameStyle.OBJECT),
144
+ type: "bundle",
145
+ name: lib.name,
146
+ icon: collect_declarations_field(lib, "icon", ""),
147
+ title: collect_declarations_field(lib, "title", lib.name),
148
+ tags: collect_declarations_field(lib, "tags", lib.descriptor?.tags),
149
+ keywords: collect_declarations_field(lib, "keywords", lib.descriptor?.keywords),
150
+ description: collect_declarations_field(lib, "description", lib.descriptor?.description),
151
+ selectors: collect_declarations_field(lib, "selectors", undefined),
152
+ data: {
153
+ package: lib.get_id(),
154
+ alias: collect_declarations_field(lib, "alias", lib.name),
155
+ namespaces: collect_declarations_field(lib, "namespaces", []),
156
+ dependencies: collect_declarations_field(lib, "dependencies", []),
157
+ distribueds: collect_declarations_field(lib, "distribueds", {}),
145
158
  }
146
- }
147
- else {
148
- $id = makeNormalizedName(lib.name, NameStyle.OBJECT);
149
- lib.log.info(`Bundle '${$id}' named from library '${lib.name}'`);
150
- }
151
- const data = {
152
- package: lib.get_id(),
153
- alias: collect_declarations_field(lib, "alias", lib.name),
154
- namespaces: collect_declarations_field(lib, "namespaces", []),
155
- dependencies: collect_declarations_field(lib, "dependencies", []),
156
- distribueds: collect_declarations_field(lib, "distribueds", {}),
157
- };
158
- if (file_desc?.data) {
159
- data.alias = file_desc.data.alias ?? data.alias;
160
- if (file_desc.data.distribueds) {
161
- Object.assign(data.distribueds, file_desc.data.distribueds);
162
- }
163
- if (Array.isArray(file_desc.data.namespaces)) {
164
- data.namespaces.push(...file_desc.data.namespaces);
165
- }
166
- if (Array.isArray(file_desc.data.dependencies)) {
167
- data.dependencies.push(...file_desc.data.dependencies);
168
- }
169
- }
170
- const manifest = {
171
- $id,
172
- type: file_desc?.type,
173
- name: file_desc?.name ?? lib.name,
174
- icon: file_desc?.icon,
175
- title: file_desc?.title,
176
- tags: file_desc?.tags,
177
- keywords: file_desc?.keywords,
178
- description: file_desc?.description ?? lib.descriptor?.description,
179
- selectors: file_desc?.selectors,
180
- data,
181
159
  };
182
- return manifest;
183
160
  }
184
161
  var Discovered;
185
162
  (function (Discovered) {
@@ -192,8 +169,9 @@ async function discover_library(ws, location, installed) {
192
169
  if (is_ignored_dir(ws, lib_path))
193
170
  return Discovered.Ignored;
194
171
  const package_path = lib_path + "/package.json";
195
- const manifest_path = findConfigFile(lib_path, "bundle.manifest");
196
- if (!file.exists(package_path) && !file.exists(manifest_path))
172
+ const has_package_manifest = file.exists(package_path);
173
+ const has_bundle_manifest = file.exists(findConfigFile(lib_path, "bundle.manifest"));
174
+ if (!has_package_manifest && !has_bundle_manifest)
197
175
  return Discovered.None;
198
176
  const lib_not_exists = ws.libraries.reduce((r, lib) => r && lib.path !== lib_path, true);
199
177
  if (!lib_not_exists)
@@ -201,8 +179,7 @@ async function discover_library(ws, location, installed) {
201
179
  const lib_desc = await readJsonFile(package_path);
202
180
  if (!lib_desc?.name)
203
181
  return Discovered.Ignored;
204
- const manifest_desc = await readConfigFile(manifest_path);
205
- if (manifest_desc || !installed) {
182
+ if (has_bundle_manifest || !installed) {
206
183
  const other = ws.get_library(lib_desc.name);
207
184
  if (other) {
208
185
  if (lib_path.includes(other.path)) {
@@ -216,22 +193,28 @@ async function discover_library(ws, location, installed) {
216
193
  const lib = new Library(lib_desc.name, lib_path, lib_desc, ws, installed);
217
194
  ws.libraries.push(lib);
218
195
  ws.log.info(`+ 📚 library: ${installed ? "⏬" : "🐣"} ${lib.get_id()} (${make_relative_path(ws.path, location)})`);
219
- // Setup library infos from bundle manifest
220
- setup_library_bundle(lib, manifest_desc);
221
- // Analyze library deployment manifest (supports json/yaml/yml/toml, singleton)
222
- if (manifest_desc?.data?.components) {
223
- for (const pub of manifest_desc.data.components) {
224
- const fpath = lib_path + "/" + (pub.ref ?? pub.id);
225
- await discover_component(lib, fpath);
226
- }
227
- }
228
196
  // Setup node search directory
229
197
  const lib_search_path = lib_path + "/node_modules";
230
198
  if (Fs.existsSync(lib_search_path)) {
231
199
  lib.search_directories.push(lib_search_path);
232
200
  }
233
- // Collect library declaration
234
- await discover_library_components(lib, lib_path);
201
+ // Load library packager
202
+ let packager = null;
203
+ if (has_bundle_manifest) {
204
+ packager = new BundledLibraryPackager();
205
+ }
206
+ else {
207
+ const declaration_config = await readSingletonConfigFile(lib_path, "declaration");
208
+ const packager_ref = declaration_config && declaration_config.data?.packager;
209
+ if (packager_ref) {
210
+ packager = await LoadLibraryPackager(packager_ref);
211
+ }
212
+ else {
213
+ packager = new DefaultLibraryPackager();
214
+ }
215
+ }
216
+ // Discover library with packager
217
+ await packager.discover_library(lib);
235
218
  return Discovered.Registered;
236
219
  }
237
220
  return Discovered.None;
@@ -0,0 +1,53 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import ChildProcess from "child_process";
10
+ function parseSpecifier(specifier) {
11
+ let raw = specifier.startsWith("npm:") ? specifier.slice(4) : specifier;
12
+ let exportName;
13
+ const hashIndex = raw.indexOf("#");
14
+ if (hashIndex >= 0) {
15
+ exportName = raw.slice(hashIndex + 1);
16
+ raw = raw.slice(0, hashIndex);
17
+ }
18
+ let packageName;
19
+ if (raw.startsWith("@")) {
20
+ const parts = raw.split("/");
21
+ packageName = parts.slice(0, 2).join("/");
22
+ }
23
+ else {
24
+ packageName = raw.split("/")[0];
25
+ }
26
+ return { importPath: raw, packageName, exportName };
27
+ }
28
+ const validPackageName = /^(@[a-z0-9\-~][a-z0-9\-._~]*\/)?[a-z0-9\-~][a-z0-9\-._~]*$/;
29
+ function resolveExport(mod, exportName) {
30
+ if (exportName) {
31
+ if (!(exportName in mod)) {
32
+ throw new Error(`Export '${exportName}' not found in module`);
33
+ }
34
+ return mod[exportName];
35
+ }
36
+ return mod.default ?? mod;
37
+ }
38
+ export async function LoadLibraryPackager(specifier) {
39
+ const { importPath, packageName, exportName } = parseSpecifier(specifier);
40
+ try {
41
+ const mod = await import(__rewriteRelativeImportExtension(importPath));
42
+ return resolveExport(mod, exportName);
43
+ }
44
+ catch {
45
+ if (!validPackageName.test(packageName)) {
46
+ throw new Error(`Invalid package name: ${packageName}`);
47
+ }
48
+ ChildProcess.execSync(`npm install ${packageName}`, { stdio: "inherit" });
49
+ const mod = await import(__rewriteRelativeImportExtension(importPath));
50
+ return resolveExport(mod, exportName);
51
+ }
52
+ }
53
+ //# sourceMappingURL=packager.js.map
@@ -0,0 +1,39 @@
1
+ import {} from "../component.js";
2
+ import { Bundle, Library } from "../workspace.js";
3
+ import { readSingletonConfigFile } from "../helpers/config-loader.js";
4
+ import {} from "../packager.js";
5
+ import { discover_component, discover_library_components, setup_library_bundle } from "../helpers/discover-workspace.js";
6
+ export class DefaultLibraryPackager {
7
+ async discover_library(lib) {
8
+ // Setup library infos from bundle manifest
9
+ setup_library_bundle(lib);
10
+ // Collect library declaration
11
+ await discover_library_components(lib, lib.path);
12
+ return lib.bundle;
13
+ }
14
+ }
15
+ export class BundledLibraryPackager {
16
+ async discover_library(lib) {
17
+ const { data: manif } = await readSingletonConfigFile(lib.path, "bundle.manifest");
18
+ const ws = lib.workspace;
19
+ let bun = ws.get_bundle(manif.$id);
20
+ if (!bun) {
21
+ bun = new Bundle(manif, lib.path, lib.workspace, lib);
22
+ lib.bundle = bun;
23
+ ws.bundles.push(bun);
24
+ lib.log.info(`+ 📦 bundle: ${bun.id}`);
25
+ }
26
+ else if (bun.source != lib) {
27
+ lib.log.warn(`Conflict between two prebuild library bundle`);
28
+ }
29
+ // Analyze library deployment manifest (supports json/yaml/yml/toml, singleton)
30
+ if (manif?.data?.components) {
31
+ for (const pub of manif.data.components) {
32
+ const fpath = lib.path + "/" + (pub.ref ?? pub.id);
33
+ await discover_component(lib, fpath);
34
+ }
35
+ }
36
+ return bun;
37
+ }
38
+ }
39
+ //# sourceMappingURL=packager-standard.js.map
package/package.json CHANGED
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "name": "@jointhedots/gear",
3
- "version": "1.1.17",
3
+ "version": "1.1.18",
4
4
  "type": "module",
5
+ "packageManager": "pnpm@10.30.3",
5
6
  "bin": {
6
7
  "jointhedots-gear": "esm/cli.js"
7
8
  },
9
+ "exports": {
10
+ "./workspace": "./esm/workspace.js"
11
+ },
8
12
  "files": [
9
13
  "esm",
10
14
  "browser-modules",
@@ -23,7 +27,7 @@
23
27
  ":build:lib:ui": "node --enable-source-maps --watch-path=esm esm/cli make --libs @jointhedots/ui --devmode --ws ../jointhedots-core/packages/jointhedots-ui",
24
28
  ":build:app": "node --enable-source-maps --watch-path=esm esm/cli make --apps playground:ui --ws ../jointhedots-core",
25
29
  "serve:mono": "node --enable-source-maps --watch-path=esm esm/cli serve --app playground:ui --devmode --port 3002 --ws ../jointhedots-core",
26
- "serve:host": "node --enable-source-maps --watch-path=esm esm/cli serve --app playground:ui:host --port 3002 --ws ../jointhedots-core",
30
+ "serve:host": "node --enable-source-maps --watch-path=esm esm/cli serve --app playground:ui:host --devmode --port 3002 --ws ../jointhedots-core",
27
31
  "serve:sfe": "node --enable-source-maps --watch-path=esm esm/cli serve --app sfe-demo --devmode --ws ../sf-explorer-app",
28
32
  "serve:demo": "node --enable-source-maps --watch-path=esm esm/cli serve --app sfe-demo --devmode --ws ../sf-explorer-demo",
29
33
  "make:sfe": "node --enable-source-maps --watch-path=esm esm/cli make --libs @sf-explorer/app --devmode --ws ../sf-explorer-app",
@@ -60,4 +64,4 @@
60
64
  "bundleDependencies": [
61
65
  "@jspm/core"
62
66
  ]
63
- }
67
+ }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes