@arc-e-tect/api-only-publisher 0.7.1 → 0.8.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.
package/src/init.js CHANGED
@@ -322,17 +322,19 @@ const CONFIG_FILE = "apionly.yaml";
322
322
  * completion is measured against; without it, apionly.yaml is compared as every
323
323
  * other file is.
324
324
  *
325
+ * @param {{paths: string, location: string}} [portfolio] the portfolio section to
326
+ * add when apionly.yaml lacks one; see addMissingConfig for what it does with it
325
327
  * @returns {{rel: string, status: "missing"|"identical"|"differs", added?: string[], text?: string}[]}
326
328
  * `added` and `text` -- the completed content -- are there only for apionly.yaml,
327
329
  * and only when something was missing from it.
328
330
  */
329
- function plan(targetDir, files, values) {
331
+ function plan(targetDir, files, values, portfolio) {
330
332
  return Object.entries(files).map(([rel, content]) => {
331
333
  const file = path.join(targetDir, rel);
332
334
  if (!fs.existsSync(file)) return { rel, status: "missing" };
333
335
  const onDisk = fs.readFileSync(file, "utf8");
334
336
  if (rel === CONFIG_FILE && values) {
335
- const completed = addMissingConfig(onDisk, { ...DEFAULTS, ...values });
337
+ const completed = addMissingConfig(onDisk, { ...DEFAULTS, ...values }, portfolio);
336
338
  if (completed.added.length > 0) {
337
339
  const status = normalised(completed.text) === normalised(content) ? "identical" : "differs";
338
340
  return { rel, status, added: completed.added, text: completed.text };
@@ -356,13 +358,17 @@ function plan(targetDir, files, values) {
356
358
  * comment already there exactly as it was. A kind already declared, however it
357
359
  * reads, is never touched -- what is already there is not changed, only what is
358
360
  * not there is added.
361
+ *
362
+ * `portfolio`, given, is added the same way, as one more slot: whether the
363
+ * library is about to have more than one target -- and so whether this is worth
364
+ * asking about at all -- is decided before init is called, not here.
359
365
  */
360
- function init(targetDir, { values = DEFAULTS, force = false, log = () => {} } = {}) {
366
+ function init(targetDir, { values = DEFAULTS, force = false, log = () => {}, portfolio = null } = {}) {
361
367
  const files = scaffold(values);
362
368
  const report = { created: [], overwritten: [], identical: [], differing: [], updated: [] };
363
369
  const updates = [];
364
370
 
365
- for (const { rel, status, added, text } of plan(targetDir, files, values)) {
371
+ for (const { rel, status, added, text } of plan(targetDir, files, values, portfolio)) {
366
372
  if (added && !force) {
367
373
  fs.writeFileSync(path.join(targetDir, rel), text);
368
374
  report.updated.push(rel);
package/src/pipeline.js CHANGED
@@ -9,7 +9,7 @@ const YAML = require("yaml");
9
9
 
10
10
  const { substituteFile } = require("./placeholders");
11
11
  const { stampFile } = require("./version");
12
- const { generateAsyncApi, isAggregate } = require("./aggregate");
12
+ const { generateAsyncApi, generateOpenApi, openapiPushDown, isAggregate } = require("./aggregate");
13
13
  const { stampFiles, componentPaths, strayPaths, fragmentStamps, unresolvedStamps, FragmentPathError, KEY } = require("./fragment-paths");
14
14
 
15
15
  class BuildError extends Error {}
@@ -85,7 +85,7 @@ function substituteTree(config, kind, log) {
85
85
  }
86
86
 
87
87
  function bundle(config, target, kind, outFile, log, { stagingRoot } = {}) {
88
- const source = config.bundlePath(target, kind, stagingRoot);
88
+ const source = config.bundleRootPath(target, kind, stagingRoot);
89
89
  if (!fs.existsSync(source)) {
90
90
  throw new BuildError(`target '${target}': bundle root not found at ${source}`);
91
91
  }
@@ -130,7 +130,7 @@ function bundleWithFragmentPaths(config, target, kind, outFile, log) {
130
130
  return YAML.parse(fs.readFileSync(file, "utf8"));
131
131
  };
132
132
 
133
- log(`-- Bundling ${path.basename(config.bundlePath(target, kind))} with ${KEY}`);
133
+ log(`-- Bundling ${path.basename(config.bundleRootPath(target, kind))} with ${KEY}`);
134
134
  const discovered = componentPaths(pass("discover", null));
135
135
  const fragments = new Set([...discovered.values()].filter(Boolean));
136
136
  const document = pass("stamp", fragments);
@@ -169,7 +169,7 @@ function bundleInlinedWithFragmentPaths(config, target, kind, outFile, log) {
169
169
  const root = path.join(scratch, "stamp");
170
170
  fs.cpSync(config.stagingRoot(kind), root, { recursive: true });
171
171
 
172
- const bundleRoot = path.relative(root, config.bundlePath(target, kind, root)).split(path.sep).join("/");
172
+ const bundleRoot = path.relative(root, config.bundleRootPath(target, kind, root)).split(path.sep).join("/");
173
173
  try {
174
174
  stampFiles(root, { except: new Set([bundleRoot]) });
175
175
  } catch (error) {
@@ -177,7 +177,7 @@ function bundleInlinedWithFragmentPaths(config, target, kind, outFile, log) {
177
177
  throw error;
178
178
  }
179
179
 
180
- log(`-- Bundling ${path.basename(config.bundlePath(target, kind))} with ${KEY}`);
180
+ log(`-- Bundling ${path.basename(config.bundleRootPath(target, kind))} with ${KEY}`);
181
181
  const file = path.join(scratch, "stamp.yaml");
182
182
  bundle(config, target, kind, file, () => {}, { stagingRoot: root });
183
183
  const document = YAML.parse(fs.readFileSync(file, "utf8"));
@@ -240,8 +240,11 @@ function prepare(config, { kinds = ["openapi", "asyncapi"], log = () => {} } = {
240
240
  stage(config, kind, log);
241
241
  substituteTree(config, kind, log);
242
242
  for (const target of targets) {
243
- if (isAggregate(config, target, kind) && kind === "asyncapi") {
243
+ if (!isAggregate(config, target, kind)) continue;
244
+ if (kind === "asyncapi") {
244
245
  generateAsyncApi(config, target, { log });
246
+ } else {
247
+ generateOpenApi(config, target, { log });
245
248
  }
246
249
  }
247
250
  }
@@ -269,14 +272,13 @@ function build(config, { targets, versionOf = () => null, kinds = ["openapi", "a
269
272
  log(`\n=== ${target} (${kind}) ===`);
270
273
  // An aggregate has no hand-written bundle root; it is synthesised from
271
274
  // its members into the staged tree, so it can never fall behind them.
275
+ let openapiOwners = null;
272
276
  if (isAggregate(config, target, kind)) {
273
- if (kind !== "asyncapi") {
274
- throw new BuildError(
275
- `target '${target}': aggregate is only supported for asyncapi; ` +
276
- `an OpenAPI portfolio is a hand-written table of contents of $refs`
277
- );
277
+ if (kind === "asyncapi") {
278
+ generateAsyncApi(config, target, { log });
279
+ } else {
280
+ openapiOwners = generateOpenApi(config, target, { log }).owners;
278
281
  }
279
- generateAsyncApi(config, target, { log });
280
282
  }
281
283
  const outFile = path.join(config.distDir(target), config.outputName(kind));
282
284
  if (config.fragmentPaths(kind)) {
@@ -284,6 +286,18 @@ function build(config, { targets, versionOf = () => null, kinds = ["openapi", "a
284
286
  } else {
285
287
  bundle(config, target, kind, outFile, log);
286
288
  }
289
+ if (openapiOwners) {
290
+ // Pass 2: only once bundling has resolved every $ref does an
291
+ // operation's id, or whether it already sets its own security,
292
+ // become visible at all.
293
+ const { text, reconciled } = openapiPushDown(fs.readFileSync(outFile, "utf8"), openapiOwners, {
294
+ operationIdStrategy: config.portfolioOperationIdStrategy(),
295
+ });
296
+ fs.writeFileSync(outFile, text);
297
+ for (const r of reconciled) {
298
+ log(`-- Reconciled tag '${r.key}': two members describe it differently; kept the first's`);
299
+ }
300
+ }
287
301
  const version = versionOf(target);
288
302
  if (version) {
289
303
  log(`-- Stamping version '${version}'`);
package/src/split.js CHANGED
@@ -111,7 +111,7 @@ function split(config, { by = "kind", outDir, log = () => {} } = {}) {
111
111
  // Union of every member's closure.
112
112
  const files = new Set();
113
113
  for (const { target, kind } of members) {
114
- const entry = config.bundlePath(target, kind);
114
+ const entry = config.bundleRootPath(target, kind);
115
115
  if (!fs.existsSync(entry)) {
116
116
  throw new SplitError(
117
117
  `target '${target}': ${kind} bundle root not found at ${entry}; build first so the tree is staged`