@gasket/plugin-docs
Advanced tools
+0
-4
@@ -1,2 +0,1 @@ | ||
| import create from './create.js'; | ||
| import configure from './configure.js'; | ||
@@ -7,3 +6,2 @@ import commands from './commands.js'; | ||
| import webpackConfig from './webpack-config.js'; | ||
| import prompt from './prompt.js'; | ||
@@ -20,6 +18,4 @@ import packageJson from '../package.json' with { type: 'json' }; | ||
| configure, | ||
| create, | ||
| commands, | ||
| metadata, | ||
| prompt, | ||
| docsSetup, | ||
@@ -26,0 +22,0 @@ webpackConfig |
+1
-23
@@ -5,4 +5,3 @@ import type { Plugin, Gasket } from '@gasket/core'; | ||
| ModuleData, | ||
| PluginData, | ||
| PresetData | ||
| PluginData | ||
| } from '@gasket/plugin-metadata'; | ||
@@ -133,4 +132,2 @@ | ||
| plugins: ModuleDocsConfig[]; | ||
| /** List of presets */ | ||
| presets: ModuleDocsConfig[]; | ||
| /** Root directory */ | ||
@@ -164,3 +161,2 @@ root: string; | ||
| * - metadata for modules not processed with plugins | ||
| * - metadata for presets | ||
| */ | ||
@@ -253,20 +249,2 @@ export function buildDocsConfigSet( | ||
| /** | ||
| * Function to add documentation configuration for a preset. | ||
| */ | ||
| export function addPreset( | ||
| /** Metadata for the preset. */ | ||
| presetData: PresetData, | ||
| /** Initial documentation setup. */ | ||
| docsSetup?: DocsSetup | ||
| ): Promise<void>; | ||
| /** | ||
| * Function to add documentation configuration for multiple presets. | ||
| */ | ||
| export function addPresets( | ||
| /** Metadata for multiple presets. */ | ||
| presetsDatas: PresetData[] | ||
| ): Promise<void>; | ||
| /** | ||
| * Function to add documentation configuration for a module. | ||
@@ -273,0 +251,0 @@ */ |
@@ -66,3 +66,2 @@ /// <reference types="@gasket/plugin-logger" /> | ||
| * - metadata for modules not processed with plugins | ||
| * - metadata for presets | ||
| * @type {import('../internal.d.ts').buildDocsConfigSet} | ||
@@ -97,3 +96,2 @@ */ | ||
| await builder.addModules(metadata.modules); | ||
| await builder.addPresets(metadata.presets); | ||
@@ -100,0 +98,0 @@ return builder.getConfigSet(); |
@@ -84,3 +84,3 @@ import { readFile, writeFile, copyFile, stat } from 'fs/promises'; | ||
| // Flatten the moduleDocsConfigs then generate | ||
| const flattened = ['plugins', 'presets', 'modules'].reduce( | ||
| const flattened = ['plugins', 'modules'].reduce( | ||
| (acc, type) => acc.concat(docsConfigSet[type]), | ||
@@ -87,0 +87,0 @@ [docsConfigSet.app] |
@@ -78,3 +78,2 @@ import path from 'node:path'; | ||
| this._plugins = []; | ||
| this._presets = []; | ||
| this._modules = []; | ||
@@ -206,3 +205,2 @@ this._transforms = []; | ||
| this._modules | ||
| .concat(this._presets) | ||
| .concat(this._plugins) | ||
@@ -297,36 +295,2 @@ .forEach((moduleDoc) => { | ||
| /** | ||
| * Add DocsConfig to the set for a preset | ||
| * @type {import('../internal.d.ts').addPreset} | ||
| */ | ||
| async addPreset(presetData, docsSetup) { | ||
| if (this._presets.find((p) => p.metadata === presetData.metadata)) return; | ||
| // If docsSetup is passed, stick with it. Otherwise, look up a docsSetup | ||
| // defined by preset. Or, see if gasket.docsSetup in package.json. Finally, | ||
| // fall back to defaults. | ||
| docsSetup = | ||
| docsSetup || | ||
| // @ts-expect-error | ||
| (presetData.module && presetData.module.docsSetup) || | ||
| getDocsSetupFromPkg(presetData) || | ||
| docsSetupDefault; | ||
| const { name } = presetData; | ||
| const targetRoot = path.join(this._docsRoot, 'presets', ...name.split('/')); | ||
| const docConfig = await this._buildDocsConfig(presetData, docsSetup, { | ||
| targetRoot | ||
| }); | ||
| this._presets.push(docConfig); | ||
| } | ||
| /** | ||
| * Add DocsConfig to the set for multiple presets if not already added | ||
| * @type {import('../internal.d.ts').addPresets} | ||
| */ | ||
| async addPresets(presetDatas) { | ||
| await Promise.all(presetDatas.map((p) => this.addPreset(p))); | ||
| } | ||
| /** | ||
| * Add DocsConfig to the set for a module | ||
@@ -384,3 +348,2 @@ * @type {import('../internal.d.ts').addModule} | ||
| plugins: sortModules(this._plugins), | ||
| presets: sortModules(this._presets), | ||
| modules: sortModules(this._modules), | ||
@@ -387,0 +350,0 @@ root: this._root, |
@@ -123,3 +123,2 @@ /* eslint-disable max-statements */ | ||
| addSection('Presets', 'All configured presets', docsConfigSet.presets); | ||
| addSection('Templates', 'All configured templates', docsConfigSet.templates); | ||
@@ -126,0 +125,0 @@ addSection('Plugins', 'All configured plugins', docsConfigSet.plugins); |
@@ -48,3 +48,3 @@ /** @typedef {import('../internal.d.ts').ModuleDocsConfig} ModuleDocsConfig */ | ||
| * - gasket scope > user scope > no scope > app plugins | ||
| * - presets > plugins > packages | ||
| * - plugins > packages | ||
| * @param {string} name - Package name | ||
@@ -70,6 +70,5 @@ * @returns {number} weight | ||
| // | ||
| const isPreset = /preset/; | ||
| const isPlugin = /plugin/; | ||
| weight += (isPreset.test(name) && 20) || (isPlugin.test(name) && 10) || 0; | ||
| weight += (isPlugin.test(name) && 10) || 0; | ||
@@ -76,0 +75,0 @@ return weight; |
@@ -54,4 +54,4 @@ import path from 'path'; | ||
| const { targetRoot } = docsConfig; | ||
| const { modules, presets, plugins } = docsConfigSet; | ||
| const allModuleDocConfigs = modules.concat(plugins).concat(presets); | ||
| const { modules, plugins } = docsConfigSet; | ||
| const allModuleDocConfigs = modules.concat(plugins); | ||
@@ -58,0 +58,0 @@ const tx = makeLinkTransform((link) => { |
+8
-14
| { | ||
| "name": "@gasket/plugin-docs", | ||
| "version": "7.5.3", | ||
| "version": "8.0.0-next.1", | ||
| "description": "Centralize doc files from plugins and modules", | ||
| "type": "module", | ||
| "main": "lib/index.js", | ||
| "types": "lib/index.d.ts", | ||
| "files": [ | ||
| "lib", | ||
| "cjs", | ||
| "EXAMPLES.md" | ||
@@ -16,4 +14,3 @@ ], | ||
| "types": "./lib/index.d.ts", | ||
| "import": "./lib/index.js", | ||
| "require": "./cjs/index.js" | ||
| "import": "./lib/index.js" | ||
| }, | ||
@@ -41,3 +38,3 @@ "./package.json": "./package.json" | ||
| "rimraf": "^6.1.2", | ||
| "@gasket/plugin-command": "^7.6.3" | ||
| "@gasket/plugin-command": "^8.0.0-next.1" | ||
| }, | ||
@@ -47,12 +44,9 @@ "devDependencies": { | ||
| "vitest": "^3.2.0", | ||
| "@gasket/cjs": "^7.1.1", | ||
| "@gasket/core": "^7.7.2", | ||
| "@gasket/plugin-git": "^7.4.8", | ||
| "@gasket/plugin-logger": "^7.4.0", | ||
| "@gasket/plugin-metadata": "^7.5.8", | ||
| "@gasket/plugin-webpack": "^7.4.0", | ||
| "create-gasket-app": "^7.4.18" | ||
| "@gasket/core": "^8.0.0-next.1", | ||
| "@gasket/plugin-logger": "^8.0.0-next.1", | ||
| "@gasket/plugin-webpack": "^8.0.0-next.1", | ||
| "@gasket/plugin-metadata": "^8.0.0-next.1", | ||
| "create-gasket-app": "^8.0.0-next.1" | ||
| }, | ||
| "scripts": { | ||
| "build": "gasket-cjs", | ||
| "lint": "eslint .", | ||
@@ -59,0 +53,0 @@ "lint:fix": "pnpm run lint --fix", |
+2
-21
| # @gasket/plugin-docs | ||
| The plugin enables the **docs** command, which centralizes doc files for the | ||
| app's plugins, presets, and supporting modules. | ||
| app's plugins and supporting modules. | ||
@@ -39,3 +39,3 @@ ## Installation | ||
| allows app developers to generate documentation for their Gasket projects. Only | ||
| those presets and plugins that are configured for a project, will be used to | ||
| those plugins that are configured for a project, will be used to | ||
| determine what documentation is available. | ||
@@ -201,21 +201,2 @@ | ||
| ## Usage | ||
| ### Presets | ||
| Presets can also set up custom docs. This is done by defining a `docsSetup` | ||
| property object on the module, which will be used to establish the `docsConfig` | ||
| for the preset. | ||
| ```js | ||
| // gasket-preset-example.js | ||
| export default { | ||
| name: 'gasket-preset-example', | ||
| docsSetup: { | ||
| link: 'OTHER.md#go-here', | ||
| files: ['more-docs/**/*.*'], | ||
| } | ||
| } | ||
| ``` | ||
| ## How it works | ||
@@ -222,0 +203,0 @@ |
| /// <reference types="@gasket/core" /> | ||
| /// <reference types="@gasket/plugin-command" /> | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| Object.defineProperty(exports, /** | ||
| * Get the docs command | ||
| * @type {import('@gasket/core').HookHandler<'commands'>} | ||
| */ "default", { | ||
| enumerable: true, | ||
| get: function() { | ||
| return commands; | ||
| } | ||
| }); | ||
| const _buildconfigset = /*#__PURE__*/ _interop_require_default(require("./utils/build-config-set.cjs")); | ||
| const _collatefiles = /*#__PURE__*/ _interop_require_default(require("./utils/collate-files.cjs")); | ||
| const _generateindex = /*#__PURE__*/ _interop_require_default(require("./utils/generate-index.cjs")); | ||
| function _interop_require_default(obj) { | ||
| return obj && obj.__esModule ? obj : { | ||
| default: obj | ||
| }; | ||
| } | ||
| function commands(gasket) { | ||
| return { | ||
| id: 'docs', | ||
| description: 'Generate docs for the app', | ||
| options: [ | ||
| { | ||
| name: 'no-view', | ||
| description: 'View the docs after generating', | ||
| type: 'boolean' | ||
| } | ||
| ], | ||
| action: async function({ view }) { | ||
| const docsConfigSet = await (0, _buildconfigset.default)(gasket); | ||
| await (0, _collatefiles.default)(docsConfigSet); | ||
| const generated = await gasket.exec('docsGenerate', docsConfigSet); | ||
| const docs = Array.isArray(generated) ? generated.flat().filter(Boolean) : [ | ||
| generated | ||
| ]; | ||
| const guides = docs.filter((gen)=>gen && !gen.name.includes('template')); | ||
| const templates = docs.filter((gen)=>gen && gen.name.includes('template')); | ||
| // Ensure guides array exists before unshifting | ||
| if (!docsConfigSet.guides) { | ||
| docsConfigSet.guides = []; | ||
| } | ||
| docsConfigSet.guides.unshift(...guides); | ||
| // Ensure templates array exists before unshifting | ||
| if (!docsConfigSet.templates) { | ||
| docsConfigSet.templates = []; | ||
| } | ||
| docsConfigSet.templates.unshift(...templates); | ||
| await (0, _generateindex.default)(docsConfigSet); | ||
| if (view) { | ||
| await gasket.exec('docsView', docsConfigSet); | ||
| } | ||
| } | ||
| }; | ||
| } |
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| Object.defineProperty(exports, /** | ||
| * Configure lifecycle to set up SW config with defaults | ||
| * @type {import('@gasket/core').HookHandler<'configure'>} | ||
| */ "default", { | ||
| enumerable: true, | ||
| get: function() { | ||
| return configure; | ||
| } | ||
| }); | ||
| const _lodashdefaultsdeep = /*#__PURE__*/ _interop_require_default(require("lodash.defaultsdeep")); | ||
| const _constants = require("./utils/constants.cjs"); | ||
| function _interop_require_default(obj) { | ||
| return obj && obj.__esModule ? obj : { | ||
| default: obj | ||
| }; | ||
| } | ||
| function configure(gasket, baseConfig) { | ||
| const userConfig = baseConfig?.docs || {}; | ||
| const docs = (0, _lodashdefaultsdeep.default)({}, userConfig, _constants.DEFAULT_CONFIG); | ||
| return { | ||
| ...baseConfig, | ||
| docs | ||
| }; | ||
| } |
| /// <reference types="create-gasket-app"/> | ||
| /// <reference types="@gasket/plugin-git" /> | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| Object.defineProperty(exports, /** @type {import('@gasket/core').HookHandler<'create'>} */ "default", { | ||
| enumerable: true, | ||
| get: function() { | ||
| return create; | ||
| } | ||
| }); | ||
| const _constants = require("./utils/constants.cjs"); | ||
| const _packagejson = /*#__PURE__*/ _interop_require_default(require("../package.json")); | ||
| function _interop_require_default(obj) { | ||
| return obj && obj.__esModule ? obj : { | ||
| default: obj | ||
| }; | ||
| } | ||
| const { name, version, devDependencies } = _packagejson.default; | ||
| function create(gasket, { pkg, gasketConfig, gitignore, typescript, useDocs, readme }) { | ||
| if (!useDocs) return; | ||
| gitignore?.add(_constants.DEFAULT_CONFIG.outputDir, 'documentation'); | ||
| gasketConfig.addCommand('docs', { | ||
| dynamicPlugins: [ | ||
| `${name}`, | ||
| '@gasket/plugin-metadata' | ||
| ] | ||
| }); | ||
| pkg.add('devDependencies', { | ||
| [name]: `^${version}`, | ||
| '@gasket/plugin-metadata': devDependencies['@gasket/plugin-metadata'] | ||
| }); | ||
| const docsScript = typescript ? 'tsx gasket.ts docs' : 'node gasket.js docs'; | ||
| pkg.add('scripts', { | ||
| docs: docsScript | ||
| }); | ||
| readme.subHeading('Documentation').content('Generated docs will be placed in the `.docs` directory. To generate markdown documentation for the API, run:').codeBlock('{{{packageManager}}} run docs', 'bash'); | ||
| } |
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| Object.defineProperty(exports, /** | ||
| * Specify what files to copy and transform | ||
| * @type {import('@gasket/core').HookHandler<'docsSetup'>} | ||
| */ "default", { | ||
| enumerable: true, | ||
| get: function() { | ||
| return docsSetup; | ||
| } | ||
| }); | ||
| const _transforms = require("./utils/transforms.cjs"); | ||
| function docsSetup() { | ||
| return { | ||
| link: 'README.md', | ||
| files: [ | ||
| 'README.md', | ||
| 'docs/**/*', | ||
| 'LICENSE.md' | ||
| ], | ||
| transforms: [ | ||
| _transforms.txGasketPackageLinks, | ||
| _transforms.txGasketUrlLinks, | ||
| _transforms.txAbsoluteLinks | ||
| ] | ||
| }; | ||
| } |
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| Object.defineProperty(exports, "default", { | ||
| enumerable: true, | ||
| get: function() { | ||
| return _default; | ||
| } | ||
| }); | ||
| const _create = /*#__PURE__*/ _interop_require_default(require("./create.cjs")); | ||
| const _configure = /*#__PURE__*/ _interop_require_default(require("./configure.cjs")); | ||
| const _commands = /*#__PURE__*/ _interop_require_default(require("./commands.cjs")); | ||
| const _metadata = /*#__PURE__*/ _interop_require_default(require("./metadata.cjs")); | ||
| const _docssetup = /*#__PURE__*/ _interop_require_default(require("./docs-setup.cjs")); | ||
| const _webpackconfig = /*#__PURE__*/ _interop_require_default(require("./webpack-config.cjs")); | ||
| const _prompt = /*#__PURE__*/ _interop_require_default(require("./prompt.cjs")); | ||
| const _packagejson = /*#__PURE__*/ _interop_require_default(require("../package.json")); | ||
| function _interop_require_default(obj) { | ||
| return obj && obj.__esModule ? obj : { | ||
| default: obj | ||
| }; | ||
| } | ||
| const { name, version, description } = _packagejson.default; | ||
| /** @type {import('@gasket/core').Plugin} */ const plugin = { | ||
| name, | ||
| version, | ||
| description, | ||
| hooks: { | ||
| configure: _configure.default, | ||
| create: _create.default, | ||
| commands: _commands.default, | ||
| metadata: _metadata.default, | ||
| prompt: _prompt.default, | ||
| docsSetup: _docssetup.default, | ||
| webpackConfig: _webpackconfig.default | ||
| } | ||
| }; | ||
| const _default = plugin; |
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| Object.defineProperty(exports, /** | ||
| * Attach additional metadata to pluginData | ||
| * @type {import('@gasket/core').HookHandler<'metadata'>} | ||
| */ "default", { | ||
| enumerable: true, | ||
| get: function() { | ||
| return metadata; | ||
| } | ||
| }); | ||
| const _constants = require("./utils/constants.cjs"); | ||
| function metadata(gasket, meta) { | ||
| const { outputDir } = gasket.config.docs || _constants.DEFAULT_CONFIG; | ||
| return { | ||
| ...meta, | ||
| commands: [ | ||
| { | ||
| name: 'docs', | ||
| description: 'Generate docs for the app', | ||
| link: 'README.md#commands' | ||
| } | ||
| ], | ||
| lifecycles: [ | ||
| { | ||
| name: 'docsSetup', | ||
| description: 'Set up what docs are captured and how to transform them', | ||
| link: 'README.md#docsSetup', | ||
| command: 'docs', | ||
| method: 'execApply' | ||
| }, | ||
| { | ||
| method: 'exec', | ||
| name: 'docsView', | ||
| description: 'View the collated documentation', | ||
| link: 'README.md#docsView', | ||
| command: 'docs', | ||
| after: 'docsSetup' | ||
| }, | ||
| { | ||
| method: 'exec', | ||
| name: 'docsGenerate', | ||
| description: 'Generate graphs for display in documation', | ||
| link: 'README.md#docsGenerate', | ||
| command: 'docs', | ||
| after: 'docsSetup' | ||
| } | ||
| ], | ||
| structures: [ | ||
| { | ||
| name: outputDir + '/', | ||
| description: 'Output of the docs command', | ||
| link: 'README.md#options' | ||
| } | ||
| ], | ||
| configurations: [ | ||
| { | ||
| name: 'docs', | ||
| link: 'README.md#configuration', | ||
| description: 'Docs config object', | ||
| type: 'object' | ||
| }, | ||
| { | ||
| name: 'docs.outputDir', | ||
| link: 'README.md#configuration', | ||
| description: 'Output directory for generated docs', | ||
| type: 'string', | ||
| default: '.docs' | ||
| } | ||
| ] | ||
| }; | ||
| } |
| {} |
| /** @type {import('@gasket/core').HookHandler<'prompt'>} */ Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| Object.defineProperty(exports, "default", { | ||
| enumerable: true, | ||
| get: function() { | ||
| return promptHook; | ||
| } | ||
| }); | ||
| async function promptHook(gasket, context, { prompt }) { | ||
| if ('useDocs' in context) return context; | ||
| const { useDocs } = await prompt([ | ||
| { | ||
| name: 'useDocs', | ||
| message: 'Do you want to use generated documentation?', | ||
| type: 'confirm' | ||
| } | ||
| ]); | ||
| return Object.assign({}, context, { | ||
| useDocs | ||
| }); | ||
| } |
| /// <reference types="@gasket/plugin-logger" /> | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| Object.defineProperty(exports, "default", { | ||
| enumerable: true, | ||
| get: function() { | ||
| return _default; | ||
| } | ||
| }); | ||
| const _configsetbuilder = /*#__PURE__*/ _interop_require_default(require("./config-set-builder.cjs")); | ||
| function _interop_require_default(obj) { | ||
| return obj && obj.__esModule ? obj : { | ||
| default: obj | ||
| }; | ||
| } | ||
| const defaults = _configsetbuilder.default.docsSetupDefault; | ||
| /** @typedef {import('@gasket/plugin-metadata').PluginData} PluginData */ /** | ||
| * Searches for the pluginData from metadata for a given plugin. | ||
| * If the plugin does not have a name, a unique match by hooks is attempted, | ||
| * otherwise a console warning is issued. | ||
| * @type {import('../internal.d.ts').findPluginData} | ||
| */ function findPluginData(plugin, pluginsDatas, logger) { | ||
| const { name } = plugin; | ||
| // If the plugin does not have a name, try to find a unique hooks match | ||
| if (!name) { | ||
| const expectedHooks = Object.keys(plugin.hooks); | ||
| const results = pluginsDatas.filter((pluginData)=>{ | ||
| const actual = Object.keys(pluginData.hooks); | ||
| return expectedHooks.length === actual.length && actual.every((k)=>expectedHooks.includes(k)); | ||
| }); | ||
| if (!results.length) { | ||
| logger.error(`Plugin missing name. Unable to find pluginData with hooks: ${JSON.stringify(expectedHooks)}`); | ||
| } else if (results.length > 1) { | ||
| logger.error(`Plugin missing name. More than one pluginData with hooks: ${JSON.stringify(expectedHooks)}`); | ||
| } else { | ||
| logger.info(`Determined plugin with missing name to be: ${results[0].name}`); | ||
| return results[0]; | ||
| } | ||
| } else { | ||
| const results = pluginsDatas.find((p)=>p.name === name); | ||
| if (!results) { | ||
| logger.error(`Unable to find pluginData for: ${name}`); | ||
| } | ||
| return results; | ||
| } | ||
| } | ||
| /** | ||
| * Processes metadata and docsSetup hooks to assemble the set of docs configs | ||
| * | ||
| * Order of operations for building docsConfig: | ||
| * - docsSetup hooked plugins | ||
| * - metadata or docsSetup lifecycle file for app | ||
| * - metadata for plugins without docsSetup hook | ||
| * - metadata for modules not processed with plugins | ||
| * - metadata for presets | ||
| * @type {import('../internal.d.ts').buildDocsConfigSet} | ||
| */ async function buildDocsConfigSet(gasket) { | ||
| const { logger } = gasket; | ||
| const metadata = await gasket.actions.getMetadata(); | ||
| const { app: appData } = metadata; | ||
| const builder = new _configsetbuilder.default(gasket); | ||
| await gasket.execApply('docsSetup', async (plugin, handler)=>{ | ||
| // If this is a lifecycle file, use it to modify the app-level docConfig | ||
| if (!plugin) { | ||
| const docsSetup = await handler({ | ||
| defaults | ||
| }); | ||
| return await builder.addApp(appData, docsSetup); | ||
| } | ||
| const pluginData = buildDocsConfigSet.findPluginData(plugin, metadata.plugins, logger); | ||
| if (pluginData) { | ||
| const docsSetup = await handler({ | ||
| defaults | ||
| }); | ||
| await builder.addPlugin(pluginData, docsSetup); | ||
| } | ||
| }); | ||
| await builder.addApp(appData); | ||
| await builder.addPlugins(metadata.plugins); | ||
| await builder.addModules(metadata.modules); | ||
| await builder.addPresets(metadata.presets); | ||
| return builder.getConfigSet(); | ||
| } | ||
| buildDocsConfigSet.findPluginData = findPluginData; | ||
| const _default = buildDocsConfigSet; |
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| Object.defineProperty(exports, "default", { | ||
| enumerable: true, | ||
| get: function() { | ||
| return _default; | ||
| } | ||
| }); | ||
| const _promises = require("fs/promises"); | ||
| const _path = /*#__PURE__*/ _interop_require_default(require("path")); | ||
| const _mkdirp = /*#__PURE__*/ _interop_require_default(require("mkdirp")); | ||
| const _rimraf = require("rimraf"); | ||
| function _interop_require_default(obj) { | ||
| return obj && obj.__esModule ? obj : { | ||
| default: obj | ||
| }; | ||
| } | ||
| /** | ||
| * Checks if a path is a file. | ||
| * @param {string} filePath - The path to check. | ||
| * @returns {Promise<boolean>} - True if it's a file, false otherwise. | ||
| */ async function isFile(filePath) { | ||
| try { | ||
| const stats = await (0, _promises.stat)(filePath); | ||
| return stats.isFile(); | ||
| } catch (err) { | ||
| console.error(`Error checking file type for: ${filePath}`, err); | ||
| return false; | ||
| } | ||
| } | ||
| /** | ||
| * Copies configured files for a module to the target output dir and applies any | ||
| * transforms. | ||
| * @param {import('../internal.d.ts').ModuleDocsConfig} moduleDocsConfig - | ||
| * @param {import('../internal.d.ts').DocsConfigSet} docsConfigSet - Configurations for | ||
| * collating docs | ||
| * @returns {Promise<void>} promise | ||
| * @private | ||
| */ async function processModule(moduleDocsConfig, docsConfigSet) { | ||
| const { transforms: gTransforms = [] } = docsConfigSet; | ||
| const { sourceRoot, targetRoot, files, transforms = [] } = moduleDocsConfig; | ||
| const allTransforms = transforms.concat(gTransforms); | ||
| await Promise.all(files.map(async (filename)=>{ | ||
| const source = _path.default.join(sourceRoot, filename); | ||
| const target = _path.default.join(targetRoot, filename); | ||
| // Check if the source is a file | ||
| const isSourceFile = await isFile(source); | ||
| if (!isSourceFile) { | ||
| console.warn(`Skipping non-file: ${source}`); | ||
| return; | ||
| } | ||
| await (0, _mkdirp.default)(_path.default.dirname(target)); | ||
| // Process all files which meet transform tests (expects UTF-8 text files) | ||
| if (allTransforms.some((tx)=>tx.test.test(source))) { | ||
| let content = await (0, _promises.readFile)(source, 'utf8'); | ||
| content = allTransforms.reduce((acc, tx)=>{ | ||
| if (tx.test.test(source)) { | ||
| return tx.handler(acc, { | ||
| filename, | ||
| docsConfig: moduleDocsConfig, | ||
| docsConfigSet | ||
| }); | ||
| } | ||
| return acc; | ||
| }, content); | ||
| return await (0, _promises.writeFile)(target, content); | ||
| } | ||
| // If file does not need transformation, just copy it | ||
| await (0, _promises.copyFile)(source, target); | ||
| })); | ||
| } | ||
| /** | ||
| * Collect and combine doc files in proper order. | ||
| * @param {import('../internal.d.ts').DocsConfigSet} docsConfigSet - Configurations for | ||
| * collating docs | ||
| * @returns {Promise<void>} promise | ||
| */ async function collateFiles(docsConfigSet) { | ||
| const { docsRoot } = docsConfigSet; | ||
| await (0, _mkdirp.default)(docsRoot); | ||
| await (0, _rimraf.rimraf)(_path.default.join(docsRoot, '*')); | ||
| // Flatten the moduleDocsConfigs then generate | ||
| const flattened = [ | ||
| 'plugins', | ||
| 'presets', | ||
| 'modules' | ||
| ].reduce((acc, type)=>acc.concat(docsConfigSet[type]), [ | ||
| docsConfigSet.app | ||
| ]); | ||
| await Promise.all(flattened.map((docsConfig)=>collateFiles.processModule(docsConfig, docsConfigSet))); | ||
| } | ||
| collateFiles.processModule = processModule; | ||
| const _default = collateFiles; |
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| Object.defineProperty(exports, "default", { | ||
| enumerable: true, | ||
| get: function() { | ||
| return _default; | ||
| } | ||
| }); | ||
| const _nodepath = /*#__PURE__*/ _interop_require_default(require("node:path")); | ||
| const _lodashdefaultsdeep = /*#__PURE__*/ _interop_require_default(require("lodash.defaultsdeep")); | ||
| const _sorts = require("./sorts.cjs"); | ||
| const _glob = require("glob"); | ||
| function _interop_require_default(obj) { | ||
| return obj && obj.__esModule ? obj : { | ||
| default: obj | ||
| }; | ||
| } | ||
| const isAppPlugin = /^\/.+\/plugins\//; | ||
| const isUrl = /^(https?:)?\/\//; | ||
| const isGasketScope = /^@gasket/; | ||
| const detailDocsHelpers = { | ||
| guides: { | ||
| sort: _sorts.sortGuides | ||
| }, | ||
| commands: { | ||
| sort: _sorts.sortCommands | ||
| }, | ||
| actions: { | ||
| sort: _sorts.sortActions | ||
| }, | ||
| structures: { | ||
| sort: _sorts.sortStructures | ||
| }, | ||
| lifecycles: { | ||
| sort: _sorts.sortLifecycles | ||
| }, | ||
| configurations: { | ||
| sort: _sorts.sortConfigurations | ||
| } | ||
| }; | ||
| /** | ||
| * Expected DetailDocsConfig types | ||
| * @type {string[]} | ||
| */ const detailDocsTypes = Object.keys(detailDocsHelpers); | ||
| /** | ||
| * Defaults for when a docsSetup is not declared. | ||
| * @type {{link: string, files: Array}} | ||
| */ const docsSetupDefault = Object.freeze({ | ||
| link: 'README.md', | ||
| files: [ | ||
| 'docs/**/*.*', | ||
| 'LICENSE.md' | ||
| ] | ||
| }); | ||
| /** | ||
| * Returns a filename with the hash removed | ||
| * @param {string} link - Filename that may have hash | ||
| * @returns {string} filename | ||
| */ const noHash = (link)=>link && link.split('#')[0]; | ||
| const getDocsSetupFromPkg = (moduleData)=>moduleData.package && moduleData.package.gasket && moduleData.package.gasket.docsSetup; | ||
| /** | ||
| * Util class for constructing the DocsConfigSet | ||
| * | ||
| * type {Class} | ||
| */ class DocsConfigSetBuilder { | ||
| /** | ||
| * Look up all doc files for a module | ||
| * @type {import('../internal.d.ts')._findAllFiles} | ||
| */ async _findAllFiles(moduleData, docsSetup, link, sourceRoot) { | ||
| if (!sourceRoot) return []; | ||
| const fileSet = new Set([]); | ||
| const tryAdd = (maybeFile)=>{ | ||
| if (Boolean(maybeFile) && !isUrl.test(maybeFile) && typeof maybeFile === 'string') { | ||
| fileSet.add(noHash(maybeFile)); | ||
| } | ||
| }; | ||
| // Add file for main doc link if not a url | ||
| tryAdd(link); | ||
| // Add files for detail meta types docs | ||
| detailDocsTypes.forEach((metaType)=>{ | ||
| const { metadata = {} } = moduleData; | ||
| if (metaType in metadata) { | ||
| metadata[metaType].forEach((o)=>{ | ||
| tryAdd(o.link); | ||
| }); | ||
| } | ||
| }); | ||
| let { files = [] } = docsSetup; | ||
| files = Array.isArray(files) ? files : [ | ||
| files | ||
| ]; | ||
| (await Promise.all(files.map(async (g)=>await (0, _glob.glob)(g, { | ||
| cwd: sourceRoot | ||
| })))).reduce((acc, cur)=>acc.concat(cur), []).forEach((file)=>tryAdd(file)); | ||
| return Array.from(fileSet); | ||
| } | ||
| /** | ||
| * Divides global and local transforms from a docsSetup. Global transforms are | ||
| * added to the top-level set. Local transforms will be added to the module's | ||
| * docConfig. | ||
| * @type {import('../internal.d.ts')._segregateTransforms} | ||
| */ _segregateTransforms(transforms) { | ||
| return transforms.reduce((acc, tx)=>{ | ||
| if (tx.global) { | ||
| this._transforms.push(tx); | ||
| } else { | ||
| acc.push(tx); | ||
| } | ||
| return acc; | ||
| }, []); | ||
| } | ||
| /** | ||
| * Constructs the DocsConfig for a module based on its info and docsSetup | ||
| * @type {import('../internal.d.ts')._buildDocsConfig} | ||
| */ async _buildDocsConfig(moduleData, docsSetup = {}, overrides = {}) { | ||
| const { name, version, // fallback to docsSetup or package.json content | ||
| link = docsSetup.link || moduleData.package && moduleData.package.homepage, description = docsSetup.description || moduleData.package && moduleData.package.description } = moduleData; | ||
| const { sourceRoot = moduleData.metadata.path } = overrides; | ||
| // Get only the local transforms | ||
| const transforms = this._segregateTransforms(docsSetup.transforms || []); | ||
| const files = await this._findAllFiles(moduleData, docsSetup, link, sourceRoot); | ||
| return { | ||
| ...docsSetup, | ||
| name, | ||
| version, | ||
| description, | ||
| // safety-check: if link wants to be a file but was not found | ||
| link: !files.includes(noHash(link)) && !isUrl.test(link) ? null : link, | ||
| sourceRoot, | ||
| ...overrides, | ||
| transforms, | ||
| files, | ||
| metadata: moduleData.metadata | ||
| }; | ||
| } | ||
| /** | ||
| * Flattens all detail types from plugins' metadata. Add a from property with | ||
| * name of parent plugin. | ||
| * @type {import('../internal.d.ts')._flattenDetails} | ||
| */ _flattenDetails(type) { | ||
| const arr = []; | ||
| this._modules.concat(this._presets).concat(this._plugins).forEach((moduleDoc)=>{ | ||
| const { sourceRoot, targetRoot, name: from } = moduleDoc; | ||
| arr.push(...(moduleDoc.metadata[type] || []).map((docsSetup)=>({ | ||
| ...docsSetup, | ||
| from, | ||
| sourceRoot, | ||
| targetRoot | ||
| }))); | ||
| }); | ||
| return detailDocsHelpers[type].sort(arr); | ||
| } | ||
| /** | ||
| * Adds additional docsSetup for modules, merging duplicates with a first in | ||
| * wins approach. When a module is then add to be configured, a docSetup will | ||
| * be looked up from what's been added by plugins here. | ||
| * @type {import('../internal.d.ts')._addModuleDocsSetup} | ||
| */ _addModuleDocsSetup(moduleDocsSetup) { | ||
| (0, _lodashdefaultsdeep.default)(this._moduleDocsSetups, moduleDocsSetup); | ||
| } | ||
| /** | ||
| * Add DocsConfig to the set for the App | ||
| * @type {import('../internal.d.ts').addApp} | ||
| */ async addApp(moduleData, docsSetup) { | ||
| // If docsSetup is passed, stick with it. Or, see if gasket.docsSetup in | ||
| // package.json. Finally, fall back to defaults. | ||
| docsSetup = docsSetup || getDocsSetupFromPkg(moduleData) || docsSetupDefault; | ||
| if (!this._app) { | ||
| const targetRoot = _nodepath.default.join(this._docsRoot, 'app'); | ||
| this._app = await this._buildDocsConfig(moduleData, docsSetup, { | ||
| targetRoot | ||
| }); | ||
| } | ||
| } | ||
| /** | ||
| * Add DocsConfig to the set for a plugin | ||
| * @type {import('../internal.d.ts').addPlugin} | ||
| */ async addPlugin(pluginData, docsSetup) { | ||
| if (this._plugins.find((p)=>p.metadata === pluginData.metadata)) return; | ||
| // If docsSetup is passed, stick with it. Or, see if gasket.docsSetup in | ||
| // package.json. Finally, fall back to defaults. | ||
| docsSetup = docsSetup || getDocsSetupFromPkg(pluginData) || docsSetupDefault; | ||
| let name = pluginData.name; | ||
| let { path: sourceRoot } = pluginData.metadata; | ||
| let targetRoot = _nodepath.default.join(this._docsRoot, 'plugins', ...name.split('/')); | ||
| if (isAppPlugin.test(pluginData.name)) { | ||
| name = _nodepath.default.relative(this._root, name); | ||
| sourceRoot = _nodepath.default.join(this._root); | ||
| targetRoot = _nodepath.default.join(this._docsRoot, 'app'); | ||
| } | ||
| const { modules, ...setup } = docsSetup; | ||
| const docConfig = await this._buildDocsConfig(pluginData, setup, { | ||
| targetRoot, | ||
| name, | ||
| sourceRoot | ||
| }); | ||
| this._plugins.push(docConfig); | ||
| if (modules) { | ||
| this._addModuleDocsSetup(modules); | ||
| } | ||
| } | ||
| /** | ||
| * Add DocsConfig to the set for multiple plugins if not already added | ||
| * @type {import('../internal.d.ts').addPlugins} | ||
| */ async addPlugins(pluginDatas) { | ||
| await Promise.all(pluginDatas.map((p)=>this.addPlugin(p))); | ||
| } | ||
| /** | ||
| * Add DocsConfig to the set for a preset | ||
| * @type {import('../internal.d.ts').addPreset} | ||
| */ async addPreset(presetData, docsSetup) { | ||
| if (this._presets.find((p)=>p.metadata === presetData.metadata)) return; | ||
| // If docsSetup is passed, stick with it. Otherwise, look up a docsSetup | ||
| // defined by preset. Or, see if gasket.docsSetup in package.json. Finally, | ||
| // fall back to defaults. | ||
| docsSetup = docsSetup || // @ts-expect-error | ||
| presetData.module && presetData.module.docsSetup || getDocsSetupFromPkg(presetData) || docsSetupDefault; | ||
| const { name } = presetData; | ||
| const targetRoot = _nodepath.default.join(this._docsRoot, 'presets', ...name.split('/')); | ||
| const docConfig = await this._buildDocsConfig(presetData, docsSetup, { | ||
| targetRoot | ||
| }); | ||
| this._presets.push(docConfig); | ||
| } | ||
| /** | ||
| * Add DocsConfig to the set for multiple presets if not already added | ||
| * @type {import('../internal.d.ts').addPresets} | ||
| */ async addPresets(presetDatas) { | ||
| await Promise.all(presetDatas.map((p)=>this.addPreset(p))); | ||
| } | ||
| /** | ||
| * Add DocsConfig to the set for a module | ||
| * @type {import('../internal.d.ts').addModule} | ||
| */ async addModule(moduleData, docsSetup) { | ||
| if (this._modules.find((p)=>p.metadata === moduleData.metadata)) return; | ||
| // If docsSetup is passed, stick with it. Otherwise, look up a docsSetup | ||
| // added by plugins. Or, see if gasket.docsSetup in package.json. Finally, | ||
| // if this is a @gasket module fall back defaults. | ||
| docsSetup = docsSetup || this._moduleDocsSetups[moduleData.name] || getDocsSetupFromPkg(moduleData) || (isGasketScope.test(moduleData.name) ? docsSetupDefault : {}); | ||
| const { name } = moduleData; | ||
| const targetRoot = _nodepath.default.join(this._docsRoot, 'modules', ...name.split('/')); | ||
| const docConfig = await this._buildDocsConfig(moduleData, docsSetup, { | ||
| targetRoot | ||
| }); | ||
| this._modules.push(docConfig); | ||
| } | ||
| /** | ||
| * Add DocsConfig to the set for multiple modules if not already added | ||
| * @type {import('../internal.d.ts').addModules} | ||
| */ async addModules(moduleDatas) { | ||
| await Promise.all(moduleDatas.map((p)=>this.addModule(p))); | ||
| } | ||
| /** | ||
| * Picks out properties to return as the config set | ||
| * @type {import('../internal.d.ts').getConfigSet} | ||
| */ getConfigSet() { | ||
| const detailDocsConfigs = detailDocsTypes.reduce((acc, metaType)=>({ | ||
| ...acc, | ||
| [metaType]: this._flattenDetails(metaType) | ||
| }), {}); | ||
| // sortByName | ||
| // - project, scoped, non-scoped | ||
| // structures dirs, files x hidden, non-hidden | ||
| return { | ||
| app: this._app, | ||
| plugins: (0, _sorts.sortModules)(this._plugins), | ||
| presets: (0, _sorts.sortModules)(this._presets), | ||
| modules: (0, _sorts.sortModules)(this._modules), | ||
| root: this._root, | ||
| docsRoot: this._docsRoot, | ||
| transforms: this._transforms, | ||
| ...detailDocsConfigs | ||
| }; | ||
| } | ||
| /** | ||
| * @param {import("@gasket/core").Gasket} gasket - Gasket API | ||
| */ constructor(gasket){ | ||
| this._plugins = []; | ||
| this._presets = []; | ||
| this._modules = []; | ||
| this._transforms = []; | ||
| const { root, docs: { outputDir } } = gasket.config; | ||
| this._root = root; | ||
| this._docsRoot = _nodepath.default.join(root, outputDir); | ||
| this._moduleDocsSetups = {}; | ||
| } | ||
| } | ||
| DocsConfigSetBuilder.docsSetupDefault = docsSetupDefault; | ||
| const _default = DocsConfigSetBuilder; |
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| Object.defineProperty(exports, "DEFAULT_CONFIG", { | ||
| enumerable: true, | ||
| get: function() { | ||
| return DEFAULT_CONFIG; | ||
| } | ||
| }); | ||
| const DEFAULT_CONFIG = { | ||
| outputDir: '.docs' | ||
| }; |
| /* eslint-disable max-statements */ Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| Object.defineProperty(exports, "default", { | ||
| enumerable: true, | ||
| get: function() { | ||
| return _default; | ||
| } | ||
| }); | ||
| const _promises = require("fs/promises"); | ||
| const _path = /*#__PURE__*/ _interop_require_default(require("path")); | ||
| const _markdowntable = /*#__PURE__*/ _interop_require_default(require("markdown-table")); | ||
| function _interop_require_default(obj) { | ||
| return obj && obj.__esModule ? obj : { | ||
| default: obj | ||
| }; | ||
| } | ||
| const isUrl = /^(https?:)?\/\//; | ||
| /** | ||
| * Generates the index README.md | ||
| * @param {import('../internal.d.ts').DocsConfigSet} docsConfigSet - Docs generation | ||
| * configs | ||
| * @returns {string} filename | ||
| */ function generateContent(docsConfigSet) { | ||
| const { app: appDocs, docsRoot } = docsConfigSet; | ||
| const refMap = new Map(); | ||
| let content = ''; | ||
| let idx = 0; | ||
| /** | ||
| * Ensure that references are unique | ||
| * @param {string | number} ref - Reference to check | ||
| * @returns {string | number} unique reference | ||
| */ function uniqueRef(ref) { | ||
| return refMap.has(ref) ? ++idx : ref; | ||
| } | ||
| const addLine = (text = '')=>{ | ||
| content += text + '\n'; | ||
| }; | ||
| const addContent = (text = '')=>{ | ||
| addLine(text); | ||
| addLine(); | ||
| }; | ||
| const addTable = (elems)=>{ | ||
| addContent((0, _markdowntable.default)(elems)); | ||
| }; | ||
| const formatLink = (link, targetRoot)=>isUrl.test(link) ? link : _path.default.relative(docsRoot, _path.default.join(targetRoot, link)); | ||
| addContent('<!-- generated by `gasket docs` -->'); | ||
| addContent('# App'); | ||
| addContent(`[${appDocs.name}] — ${appDocs.description}`); | ||
| refMap.set(appDocs.name, formatLink(appDocs.link, appDocs.targetRoot)); | ||
| const addSection = (sectionTitle, sectionDesc, docs, { includeVersion = true, additionalHeaders = [], linkFallbacks = false } = {})=>{ | ||
| if (!docs || !docs.length) return; | ||
| addContent(`## ${sectionTitle}`); | ||
| addContent(sectionDesc); | ||
| const headers = includeVersion ? [ | ||
| 'Name', | ||
| 'Version', | ||
| 'Description' | ||
| ].concat(additionalHeaders) : [ | ||
| 'Name', | ||
| 'Description' | ||
| ].concat(additionalHeaders); | ||
| addTable([ | ||
| headers, | ||
| ...docs.map((moduleDoc)=>{ | ||
| const additionalHeaderValues = additionalHeaders.map((h)=>moduleDoc[h.toLowerCase()]); | ||
| const { name, description, link, version, targetRoot, deprecated } = moduleDoc; | ||
| let itemName = deprecated ? `${name} (deprecated)` : name; | ||
| if (link || linkFallbacks) { | ||
| const ref = uniqueRef(itemName); | ||
| itemName = ref === name ? `[${itemName}]` : `[${itemName}][${ref}]`; | ||
| refMap.set(ref, formatLink(link || 'README.md', targetRoot)); | ||
| } | ||
| return [ | ||
| itemName, | ||
| ...includeVersion ? [ | ||
| version, | ||
| description, | ||
| ...additionalHeaderValues | ||
| ] : [ | ||
| description, | ||
| ...additionalHeaderValues | ||
| ] | ||
| ]; | ||
| }) | ||
| ]); | ||
| }; | ||
| addSection('Guides', 'Help and explanations docs', docsConfigSet.guides, { | ||
| includeVersion: false | ||
| }); | ||
| addSection('Commands', 'Available commands', docsConfigSet.commands, { | ||
| includeVersion: false | ||
| }); | ||
| addSection('Actions', 'Available actions', docsConfigSet.actions, { | ||
| includeVersion: false | ||
| }); | ||
| addSection('Lifecycles', 'Available lifecycles', docsConfigSet.lifecycles, { | ||
| includeVersion: false | ||
| }); | ||
| addSection('Structures', 'Available structure', docsConfigSet.structures, { | ||
| includeVersion: false | ||
| }); | ||
| addSection('Presets', 'All configured presets', docsConfigSet.presets); | ||
| addSection('Templates', 'All configured templates', docsConfigSet.templates); | ||
| addSection('Plugins', 'All configured plugins', docsConfigSet.plugins); | ||
| addSection('Modules', 'Dependencies and supporting modules', docsConfigSet.modules); | ||
| addSection('Configurations', 'Available configuration options in the `gasket.js`', docsConfigSet.configurations, { | ||
| includeVersion: false, | ||
| additionalHeaders: [ | ||
| 'Type', | ||
| 'Default' | ||
| ], | ||
| linkFallbacks: true | ||
| }); | ||
| addContent('<!-- LINKS -->'); | ||
| refMap.forEach(function(value, key) { | ||
| addLine('[' + key + ']:' + value); | ||
| }); | ||
| return content; | ||
| } | ||
| /** | ||
| * Generates the index README.md | ||
| * @param {import('../internal.d.ts').DocsConfigSet} docsConfigSet - Docs generation | ||
| * configs | ||
| * @returns {Promise<string>} filename | ||
| */ async function generateIndex(docsConfigSet) { | ||
| const { docsRoot } = docsConfigSet; | ||
| const target = _path.default.join(docsRoot, 'README.md'); | ||
| const content = await generateIndex.generateContent(docsConfigSet); | ||
| await (0, _promises.writeFile)(target, content); | ||
| return target; | ||
| } | ||
| generateIndex.generateContent = generateContent; | ||
| const _default = generateIndex; |
| /** @typedef {import('../internal.d.ts').ModuleDocsConfig} ModuleDocsConfig */ /** | ||
| * Sort an array of names alphabetically. | ||
| * @param {string} a - Name | ||
| * @param {string} b - Name | ||
| * @returns {number} comparison | ||
| */ Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| function _export(target, all) { | ||
| for(var name in all)Object.defineProperty(target, name, { | ||
| enumerable: true, | ||
| get: all[name] | ||
| }); | ||
| } | ||
| _export(exports, { | ||
| sortActions: function() { | ||
| return sortActions; | ||
| }, | ||
| sortCommands: function() { | ||
| return sortCommands; | ||
| }, | ||
| sortConfigurations: function() { | ||
| return sortConfigurations; | ||
| }, | ||
| sortGuides: function() { | ||
| return sortGuides; | ||
| }, | ||
| sortLifecycles: function() { | ||
| return sortLifecycles; | ||
| }, | ||
| sortModules: function() { | ||
| return sortModules; | ||
| }, | ||
| sortStructures: function() { | ||
| return sortStructures; | ||
| } | ||
| }); | ||
| function alphaCompare(a, b) { | ||
| if (a === b) return 0; | ||
| return a > b ? 1 : -1; | ||
| } | ||
| /** | ||
| * Makes a sort compare function that looks up a names weight | ||
| * @param {Function} getWeight - Weight lookup callback | ||
| * @returns {Function} compare | ||
| */ function makeWeightedCompare(getWeight) { | ||
| return function weightedCompare(a, b) { | ||
| const aWeight = getWeight(a); | ||
| const bWeight = getWeight(b); | ||
| if (aWeight !== bWeight) { | ||
| return aWeight > bWeight ? -1 : 1; | ||
| } | ||
| return alphaCompare(a, b); | ||
| }; | ||
| } | ||
| /** | ||
| * Returns a function to sort an array of objects by a given key and compare | ||
| * function | ||
| * @param {string} key - Property to sort by | ||
| * @param {Function} compare - Sort function | ||
| * @returns {function(Array):Array} sorter | ||
| */ function sortByKey(key, compare) { | ||
| return function sorter(arr) { | ||
| return arr.sort((a, b)=>compare(a[key], b[key])); | ||
| }; | ||
| } | ||
| /** | ||
| * Determine the weight of a package name by scope, followed by type. | ||
| * - gasket scope > user scope > no scope > app plugins | ||
| * - presets > plugins > packages | ||
| * @param {string} name - Package name | ||
| * @returns {number} weight | ||
| */ function getModuleWeight(name) { | ||
| // | ||
| // weight from scope | ||
| // | ||
| const isGasketScope = /^@gasket/; | ||
| const isUserScope = /^@/; | ||
| const isAppPlugin = /^plugins\//; | ||
| let weight = isGasketScope.test(name) && 300 || isUserScope.test(name) && 200 || !isAppPlugin.test(name) && 100 || 0; | ||
| // | ||
| // weight from type | ||
| // | ||
| const isPreset = /preset/; | ||
| const isPlugin = /plugin/; | ||
| weight += isPreset.test(name) && 20 || isPlugin.test(name) && 10 || 0; | ||
| return weight; | ||
| } | ||
| /** | ||
| * Sort an array of modules by name | ||
| * @type {function(ModuleDocsConfig[]): ModuleDocsConfig[]} | ||
| */ const sortModules = sortByKey('name', makeWeightedCompare(getModuleWeight)); | ||
| /** | ||
| * Use the same module weight by name, yet cli is always first | ||
| * @param {string} name - Package name | ||
| * @returns {number} weight | ||
| */ function getGuideWeight(name) { | ||
| const isCli = /^@gasket\/cli/; | ||
| return isCli.test(name) && 1000 || getModuleWeight(name); | ||
| } | ||
| /** | ||
| * Sort an array of guides by the module their from | ||
| * @type {function(ModuleDocsConfig[]): ModuleDocsConfig[]} | ||
| */ const sortGuides = sortByKey('from', makeWeightedCompare(getGuideWeight)); | ||
| /** | ||
| * Determine the weight of a structure name by its type. | ||
| * - hidden dirs > dirs > hidden files > files | ||
| * @param {string} name - Structure name | ||
| * @returns {number} weight | ||
| */ function getStructureWeight(name) { | ||
| const isDir = /\/$/; | ||
| const isHidden = /^\./; | ||
| return isDir.test(name) && isHidden.test(name) && 1000 || isDir.test(name) && 100 || isHidden.test(name) && 10 || 0; | ||
| } | ||
| /** | ||
| * Sort an array of structures by name | ||
| * @type {function(import('../internal.d.ts').DetailDocsConfig[]): | ||
| * import('../internal.d.ts').DetailDocsConfig[]} | ||
| */ const sortStructures = sortByKey('name', makeWeightedCompare(getStructureWeight)); | ||
| /** | ||
| * Sort an array of commands by name | ||
| * @type {function(import('../internal.d.ts').DetailDocsConfig[]): | ||
| * import('../internal.d.ts').DetailDocsConfig[]} | ||
| */ const sortCommands = sortByKey('name', alphaCompare); | ||
| /** | ||
| * Sort an array of actions by name | ||
| * @type {function(import('../internal.d.ts').DetailDocsConfig[]): | ||
| * import('../internal.d.ts').DetailDocsConfig[]} | ||
| */ const sortActions = sortByKey('name', alphaCompare); | ||
| /** | ||
| * Sort an array of lifeycles by name | ||
| * @type {function(import('../internal.d.ts').DetailDocsConfig[]): | ||
| * import('../internal.d.ts').DetailDocsConfig[]} | ||
| */ // TODO (kinetifex): eventually sort by parent and order when doing graphing | ||
| // work | ||
| const sortLifecycles = sortByKey('name', alphaCompare); | ||
| /** | ||
| * Sort an array of configurations by name | ||
| * @type {function(import('../internal.d.ts').DetailDocsConfig[]): | ||
| * import('../internal.d.ts').DetailDocsConfig[]} | ||
| */ const sortConfigurations = sortByKey('name', alphaCompare); |
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| function _export(target, all) { | ||
| for(var name in all)Object.defineProperty(target, name, { | ||
| enumerable: true, | ||
| get: all[name] | ||
| }); | ||
| } | ||
| _export(exports, { | ||
| makeLinkTransform: function() { | ||
| return makeLinkTransform; | ||
| }, | ||
| txAbsoluteLinks: function() { | ||
| return txAbsoluteLinks; | ||
| }, | ||
| txGasketPackageLinks: function() { | ||
| return txGasketPackageLinks; | ||
| }, | ||
| txGasketUrlLinks: function() { | ||
| return txGasketUrlLinks; | ||
| } | ||
| }); | ||
| const _path = /*#__PURE__*/ _interop_require_default(require("path")); | ||
| function _interop_require_default(obj) { | ||
| return obj && obj.__esModule ? obj : { | ||
| default: obj | ||
| }; | ||
| } | ||
| const isGasketScope = /^@gasket/; | ||
| const isMarkdown = /\.md$/; | ||
| const matchLink = /(]\s?:\s?|]\()([^)\s]+)(\)|\s?)/g; | ||
| const matchUrlLink = /^https:\/\/github.com\/godaddy\/gasket\/tree\/[^/]+\/packages\/(gasket-[^/]+)(\/.+)/; | ||
| /** | ||
| * Creates transform to modify links in markdown files | ||
| * @type {import('../internal.d.ts').LinkTransform} | ||
| */ const makeLinkTransform = (callback)=>(content)=>{ | ||
| return content.replace(matchLink, (match, p1, p2, p3)=>{ | ||
| return [ | ||
| p1, | ||
| callback(p2) || p2, | ||
| p3 | ||
| ].join(''); | ||
| }); | ||
| }; | ||
| /** | ||
| * Updates gasket monorepo /packages/* links to be URLs to repo. | ||
| * @type {import('../internal.d.ts').DocsTransform} | ||
| */ const txGasketPackageLinks = { | ||
| global: true, | ||
| test: /(node_modules\/@gasket|packages\/gasket-.+)\/.+\.md$/, | ||
| handler: function txGasketPackageLinks(content, { docsConfig }) { | ||
| // safety check that this is a @gasket scoped package. | ||
| if (!isGasketScope.test(docsConfig.name)) return content; | ||
| const tx = makeLinkTransform((link)=>{ | ||
| if (/^\/packages\/gasket-.+/.test(link)) { | ||
| return [ | ||
| 'https://github.com/godaddy/gasket/tree/main', | ||
| link | ||
| ].join(''); | ||
| } | ||
| }); | ||
| return tx(content); | ||
| } | ||
| }; | ||
| /** | ||
| * Updates all gasket URL links to be relative to the collated docs if the | ||
| * target package has a docsConfig. | ||
| * @type {import('../internal.d.ts').DocsTransform} | ||
| */ const txGasketUrlLinks = { | ||
| global: true, | ||
| test: isMarkdown, | ||
| /** @type {import('../internal.d.ts').DocsTransformHandler} */ handler: function txGasketUrlLinks(content, { filename, docsConfig, docsConfigSet }) { | ||
| const { targetRoot } = docsConfig; | ||
| const { modules, presets, plugins } = docsConfigSet; | ||
| const allModuleDocConfigs = modules.concat(plugins).concat(presets); | ||
| const tx = makeLinkTransform((link)=>{ | ||
| return link.replace(matchUrlLink, (match, pkgMatch, fileMatch)=>{ | ||
| const moduleName = pkgMatch.replace('gasket-', '@gasket/'); | ||
| const tgtDocConfig = allModuleDocConfigs.find(function(m) { | ||
| if (typeof m === 'object') { | ||
| return m.name === moduleName; | ||
| } | ||
| }); | ||
| if (tgtDocConfig) { | ||
| const dirWithLinkRef = _path.default.dirname(_path.default.join(targetRoot, filename)); | ||
| const targetRootConfig = typeof tgtDocConfig === 'object' ? tgtDocConfig.targetRoot : tgtDocConfig; | ||
| const filePathOfLink = _path.default.join(targetRootConfig, fileMatch); | ||
| return _path.default.relative(dirWithLinkRef, filePathOfLink); | ||
| } | ||
| return match; | ||
| }); | ||
| }); | ||
| return tx(content); | ||
| } | ||
| }; | ||
| /** | ||
| * Updates any absolute links in collated packages to be relative to the | ||
| * markdown file itself. | ||
| * @type {import('../internal.d.ts').DocsTransform} | ||
| */ const txAbsoluteLinks = { | ||
| global: true, | ||
| test: isMarkdown, | ||
| handler: function txAbsoluteLinks(content, { filename, docsConfig }) { | ||
| const { targetRoot } = docsConfig; | ||
| const dirname = _path.default.dirname(_path.default.join(targetRoot, filename)); | ||
| const tx = makeLinkTransform((link)=>{ | ||
| return link.replace(/(^\/.+)(#.+)/, (match, p1, p2)=>{ | ||
| const linkTarget = _path.default.join(targetRoot, p1); | ||
| const relLink = _path.default.relative(dirname, linkTarget); | ||
| return [ | ||
| relLink, | ||
| p2 | ||
| ].join(''); | ||
| }); | ||
| }); | ||
| return tx(content); | ||
| } | ||
| }; |
| /// <reference types="@gasket/plugin-webpack" /> | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| Object.defineProperty(exports, /** @type {import('@gasket/core').HookHandler<'webpackConfig'>} */ "default", { | ||
| enumerable: true, | ||
| get: function() { | ||
| return webpackConfigHook; | ||
| } | ||
| }); | ||
| const _packagejson = /*#__PURE__*/ _interop_require_default(require("../package.json")); | ||
| function _interop_require_default(obj) { | ||
| return obj && obj.__esModule ? obj : { | ||
| default: obj | ||
| }; | ||
| } | ||
| const { name } = _packagejson.default; | ||
| function webpackConfigHook(gasket, webpackConfig) { | ||
| webpackConfig.resolve.alias[name] = false; | ||
| return webpackConfig; | ||
| } |
| /// <reference types="create-gasket-app"/> | ||
| /// <reference types="@gasket/plugin-git" /> | ||
| import { DEFAULT_CONFIG } from './utils/constants.js'; | ||
| import packageJson from '../package.json' with { type: 'json' }; | ||
| const { name, version, devDependencies } = packageJson; | ||
| /** @type {import('@gasket/core').HookHandler<'create'>} */ | ||
| export default function create(gasket, { | ||
| pkg, | ||
| gasketConfig, | ||
| gitignore, | ||
| typescript, | ||
| useDocs, | ||
| readme | ||
| }) { | ||
| if (!useDocs) return; | ||
| gitignore?.add(DEFAULT_CONFIG.outputDir, 'documentation'); | ||
| gasketConfig.addCommand('docs', { | ||
| dynamicPlugins: [ | ||
| `${name}`, | ||
| '@gasket/plugin-metadata' | ||
| ] | ||
| }); | ||
| pkg.add('devDependencies', { | ||
| [name]: `^${version}`, | ||
| '@gasket/plugin-metadata': devDependencies['@gasket/plugin-metadata'] | ||
| }); | ||
| const docsScript = typescript | ||
| ? 'tsx gasket.ts docs' | ||
| : 'node gasket.js docs'; | ||
| pkg.add('scripts', { | ||
| docs: docsScript | ||
| }); | ||
| readme.subHeading('Documentation') | ||
| .content('Generated docs will be placed in the `.docs` directory. To generate markdown documentation for the API, run:') | ||
| .codeBlock('{{{packageManager}}} run docs', 'bash'); | ||
| } |
| /** @type {import('@gasket/core').HookHandler<'prompt'>} */ | ||
| export default async function promptHook(gasket, context, { prompt }) { | ||
| if ('useDocs' in context) return context; | ||
| const { useDocs } = await prompt([ | ||
| { | ||
| name: 'useDocs', | ||
| message: 'Do you want to use generated documentation?', | ||
| type: 'confirm' | ||
| } | ||
| ]); | ||
| return Object.assign({}, context, { useDocs }); | ||
| } |
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
7
-22.22%5
-44.44%51461
-48.13%19
-48.65%1310
-49.6%1
Infinity%223
-7.85%+ Added
+ Added
+ Added
- Removed
- Removed
- Removed