@gasket/plugin-metadata
Advanced tools
Comparing version 7.2.0-canary.20 to 7.2.0
@@ -1,4 +0,22 @@ | ||
const path = require('path'); | ||
import path from 'path'; | ||
import { createRequire } from 'module'; | ||
const require = createRequire(import.meta.url); | ||
const isModulePath = /^[/.]|^[a-zA-Z]:\\|node_modules/; | ||
const isGasketModule = /(@gasket\/|gasket-)(?!plugin)(?!preset).+/; | ||
const isGasketPreset = /(gasket-preset)|(@gasket\/preset-)/; | ||
const isGasketPlugin = /(gasket-plugin)|(@gasket\/plugin-)/; | ||
let _metadata; | ||
function tryRequire(tryPath) { | ||
try { | ||
return require(tryPath); | ||
} catch (err) { | ||
if (err.code === 'MODULE_NOT_FOUND') { | ||
return null; | ||
} | ||
throw err; | ||
} | ||
} | ||
function getAppInfo(gasket) { | ||
@@ -13,8 +31,12 @@ const { config: { root } } = gasket; | ||
app = { | ||
path: path.dirname(pkgPath), | ||
package: pkg, | ||
version: pkg.version, | ||
name: pkg.name | ||
name: pkg.name, | ||
metadata: { | ||
name: pkg.name, | ||
path: path.dirname(pkgPath) | ||
} | ||
}; | ||
} catch (err) { | ||
// eslint-disable-next-line no-console | ||
console.error(`Error loading app metadata: ${err.message}`); | ||
@@ -26,27 +48,72 @@ } | ||
/** @type {import('@gasket/core').HookHandler<'actions'>} */ | ||
module.exports = function actions(gasket) { | ||
return { | ||
getMetadata: async function getMetadata() { | ||
const app = getAppInfo(gasket); | ||
const plugins = []; | ||
const modules = []; | ||
const presets = []; | ||
/** @type {import('@gasket/core').ActionHandler<'getMetadata'>} */ | ||
async function getMetadata(gasket) { | ||
if (!_metadata) { | ||
const app = getAppInfo(gasket); | ||
const plugins = []; | ||
const presets = []; | ||
const modules = {}; | ||
await gasket.execApply('metadata', async (data, handler) => { | ||
const pluginData = await handler(data); | ||
pluginData.path = path.dirname(path.join(require.resolve(pluginData.name), '..')); | ||
// eslint-disable-next-line max-statements | ||
await gasket.execApply('metadata', async (plugin, handler) => { | ||
const isPreset = isGasketPreset.test(plugin.name); | ||
const isPlugin = isGasketPlugin.test(plugin.name); | ||
const isGasketPackage = isPlugin || isPreset; | ||
const pluginData = { | ||
...plugin, | ||
metadata: (await handler({ name: plugin.name })) | ||
}; | ||
if (!isGasketPackage) { | ||
pluginData.metadata.path = path.join(app.metadata.path, 'plugins'); | ||
plugins.push(pluginData); | ||
} else { | ||
let resolvedPath; | ||
try { | ||
resolvedPath = require.resolve(pluginData.name, { paths: [gasket.config.root] }); | ||
} catch (error) { | ||
gasket.logger.error(`Error resolving plugin ${pluginData.name}: ${error.message}`); | ||
return; | ||
} | ||
pluginData.metadata.path = path.dirname(path.join(resolvedPath, '..')); | ||
const { dependencies, devDependencies } = require(path.join(pluginData.metadata.path, 'package.json')); | ||
if (pluginData.modules) { | ||
const moduleData = pluginData.modules.map(m => { | ||
return { ...m, path: path.dirname(path.join(require.resolve(m.name), '..')) }; | ||
}); | ||
modules.push(...moduleData); | ||
if (isPreset) { | ||
presets.push(pluginData); | ||
} else { | ||
plugins.push(pluginData); | ||
} | ||
}); | ||
return { app, plugins, modules, presets }; | ||
} | ||
}; | ||
for (const name of Object.keys({ ...dependencies, ...devDependencies })) { | ||
const isModule = isGasketModule.test(name); | ||
// eslint-disable-next-line no-continue | ||
if (!isModule) continue; | ||
// | ||
// get gasket module details if installed | ||
// | ||
const mod = tryRequire(path.join(name, 'package.json')); | ||
if (mod) { | ||
modules[name] = { | ||
name: mod.name, | ||
version: mod.version, | ||
description: mod.description, | ||
metadata: { | ||
link: 'README.md', | ||
path: path.dirname(path.join(require.resolve(name), '..')) | ||
} | ||
}; | ||
} | ||
} | ||
} | ||
}); | ||
_metadata = { app, plugins, modules: Object.values(modules), presets }; | ||
} | ||
return _metadata; | ||
} | ||
export default { | ||
getMetadata | ||
}; |
@@ -0,5 +1,7 @@ | ||
import { createRequire } from 'module'; | ||
const require = createRequire(import.meta.url); | ||
const { name, version } = require('../package.json'); | ||
/** @type {import('@gasket/core').HookHandler<'create'>} */ | ||
module.exports = function create(gasket, { pkg, gasketConfig }) { | ||
export default function create(gasket, { pkg, gasketConfig }) { | ||
gasketConfig.addPlugin('pluginMetadata', name); | ||
@@ -9,2 +11,2 @@ pkg.add('dependencies', { | ||
}); | ||
}; | ||
} |
@@ -1,2 +0,2 @@ | ||
import type { MaybeAsync } from '@gasket/core'; | ||
import type { MaybeAsync, Plugin } from '@gasket/core'; | ||
import type { PackageJson } from 'create-gasket-app'; | ||
@@ -26,2 +26,4 @@ | ||
description?: string; | ||
metadata?: Record<string, any>; | ||
} | ||
@@ -40,6 +42,9 @@ | ||
/** Actions enabled by this plugin */ | ||
actions?: Array<DetailData>; | ||
/** App files and directories used by plugin */ | ||
structures?: Array<DetailData>; | ||
/** Configuration options for gasket.config.js */ | ||
/** Configuration options for gasket.js */ | ||
configurations?: Array<ConfigurationsData>; | ||
@@ -71,2 +76,5 @@ | ||
/** Version of the module */ | ||
version?: string; | ||
/** Description of the module or element */ | ||
@@ -73,0 +81,0 @@ description?: string; |
@@ -1,13 +0,17 @@ | ||
const create = require('./create'); | ||
const actions = require('./actions'); | ||
import create from './create.js'; | ||
import actions from './actions.js'; | ||
import webpackConfig from './webpack-config.js'; | ||
import { createRequire } from 'module'; | ||
const require = createRequire(import.meta.url); | ||
const { name, version, description } = require('../package.json'); | ||
/** @type {import('@gasket/core').Plugin} */ | ||
module.exports = { | ||
export default ({ | ||
name, | ||
version, | ||
description, | ||
actions, | ||
hooks: { | ||
create, | ||
actions, | ||
webpackConfig, | ||
metadata(gasket, meta) { | ||
@@ -17,2 +21,9 @@ const mod = require('@gasket/core/package.json'); | ||
...meta, | ||
actions: [ | ||
{ | ||
name: 'getMetadata', | ||
description: 'Get the metadata for the plugins & modules', | ||
link: 'README.md#getMetadata' | ||
} | ||
], | ||
lifecycles: [{ | ||
@@ -36,2 +47,2 @@ name: 'metadata', | ||
} | ||
}; | ||
}); |
{ | ||
"name": "@gasket/plugin-metadata", | ||
"version": "7.2.0-canary.20", | ||
"version": "7.2.0", | ||
"description": "Adds metadata to gasket lifecycles", | ||
"type": "module", | ||
"main": "lib/index.js", | ||
"types": "lib/index.d.ts", | ||
"files": [ | ||
"docs", | ||
"lib" | ||
@@ -14,8 +14,6 @@ ], | ||
"lint:fix": "npm run lint -- --fix", | ||
"test": "cross-env NODE_OPTIONS='--unhandled-rejections=strict' jest", | ||
"test:watch": "jest --watch", | ||
"test:coverage": "jest --coverage", | ||
"test": "vitest --watch=false --globals", | ||
"test:watch": "npm run test -- --watch", | ||
"test:coverage": "npm run test -- --coverage", | ||
"posttest": "npm run lint && npm run typecheck", | ||
"disabled_prepack": "npm run docs", | ||
"disabled_docs": "jsdoc2md --plugin @godaddy/dmd --files lib/*.js > docs/api.md", | ||
"typecheck": "tsc", | ||
@@ -37,5 +35,2 @@ "typecheck:watch": "tsc --watch" | ||
"author": "GoDaddy Operating Company, LLC", | ||
"maintainers": [ | ||
"Andrew Gerard <agerard@godaddy.com>" | ||
], | ||
"license": "MIT", | ||
@@ -47,17 +42,15 @@ "bugs": { | ||
"dependencies": { | ||
"@gasket/core": "^7.2.0-canary.20" | ||
"@gasket/core": "^7.2.0", | ||
"@gasket/plugin-logger": "^7.2.0" | ||
}, | ||
"devDependencies": { | ||
"@godaddy/dmd": "^1.0.4", | ||
"@types/jest": "^29.5.12", | ||
"@types/node": "^20.12.5", | ||
"cross-env": "^7.0.3", | ||
"eslint": "^8.56.0", | ||
"eslint-config-godaddy": "^7.1.0", | ||
"eslint-plugin-jest": "^27.6.3", | ||
"eslint-config-godaddy": "^7.1.1", | ||
"eslint-plugin-jest": "^28.6.0", | ||
"eslint-plugin-json": "^3.1.0", | ||
"eslint-plugin-unicorn": "^44.0.0", | ||
"jest": "^29.7.0", | ||
"jsdoc-to-markdown": "^7.1.0", | ||
"typescript": "^5.4.5" | ||
"eslint-plugin-unicorn": "^55.0.0", | ||
"typescript": "^5.4.5", | ||
"vitest": "^2.1.5" | ||
}, | ||
@@ -67,6 +60,8 @@ "eslintConfig": { | ||
"godaddy", | ||
"plugin:jest/recommended" | ||
"plugin:jest/recommended", | ||
"plugin:jsdoc/recommended-typescript-flavor" | ||
], | ||
"plugins": [ | ||
"unicorn" | ||
"unicorn", | ||
"jsdoc" | ||
], | ||
@@ -77,3 +72,3 @@ "rules": { | ||
}, | ||
"gitHead": "abdb788c7ff44f4c6db7a5885e96e2dd315273fc" | ||
"gitHead": "8790fd065f4bcb853fc9a2deecf0833999f41443" | ||
} |
112
README.md
# @gasket/plugin-metadata | ||
Metadata is the information about the register plugins and presets, available to | ||
Metadata is the information about the registered plugins and presets, available to | ||
plugin lifecycle hooks. This data can be used in various was for plugins, most | ||
@@ -9,4 +9,14 @@ notably the [@gasket/plugin-docs] which uses it to collate docs for an app. | ||
This is a default plugin in the Gasket CLI and is always available for use. | ||
This is a default plugin in newly create Gasket apps. | ||
## Actions | ||
### getMetadata | ||
Get all the metadata for the configured plugins and modules. | ||
```js | ||
const metadata = await gasket.actions.getMetadata(); | ||
``` | ||
## Lifecycles | ||
@@ -26,3 +36,3 @@ | ||
// gasket-plugin-example.js | ||
module.exports = { | ||
export default { | ||
name: 'example', | ||
@@ -50,4 +60,8 @@ hooks: { | ||
modules: [ | ||
'left-pad', | ||
{ name: 'right-pad', extra: 'data', link: 'DOC.md' } | ||
{ | ||
name: 'module-name', | ||
version: '7.0.0', | ||
description: 'module-name despcrition', | ||
link: 'README.md' | ||
} | ||
] | ||
@@ -75,19 +89,4 @@ } | ||
Beside the lifecycles available to plugins, metadata can also be described for | ||
preset and modules. | ||
modules. | ||
### Presets | ||
Presets can describe additional metadata. This is done by defining a `metadata` | ||
property object on the module, which will get expanded to the [PresetData]. | ||
```js | ||
// gasket-preset-example.js | ||
module.exports = { | ||
require, | ||
metadata: { | ||
extra: 'information' | ||
} | ||
} | ||
``` | ||
### Modules | ||
@@ -122,4 +121,4 @@ | ||
Plugins and apps can read from the [metadata object] by accessing | ||
`gasket.metadata` in most lifecycles. | ||
Plugins and apps can read from the [metadata object] by using | ||
the `getMetadata` Gasket action. | ||
@@ -129,36 +128,15 @@ #### Access example | ||
Back to our example plugin, let's see how we can access details about an | ||
installed module, and put is some conditional logic. | ||
installed module, and put is some conditional logic. | ||
```js | ||
// gasket-plugin-example.js | ||
const semver = require('semver') | ||
import semver from 'semver'; | ||
module.exports = { | ||
export default { | ||
name: 'example', | ||
hooks: { | ||
// Because metadata is collected during the init lifecycle, we must | ||
// adjust our init hook to occur after in order to read the metadata | ||
init: { | ||
timing: { | ||
after: ['@gasket/metadata'] | ||
}, | ||
handler: function initHook(gasket) { | ||
const { metadata } = gasket; | ||
// Find the ModuleData for a package | ||
const moduleData = metadata.modules.find(mod => mod.name === 'some-package'); | ||
// If it is installed, and meets requires | ||
if(moduleData && semver.satisfies(moduleData.version, '13.x')) { | ||
// Do something special | ||
} else { | ||
// Skip and issue warning about upgrading | ||
} | ||
// Find the PluginData for a plugin | ||
const pluginData = metadata.plugins.find(mod => mod.name === 'gasket-plugin-feature'); | ||
if(pluginData) { | ||
// Fallback for when a certain plugin is not installed | ||
} | ||
} | ||
// We need access to metadata during build | ||
async build(gasket) { | ||
const metadata = await gasket.actions.getMetadata() | ||
// ... use metadata | ||
} | ||
@@ -169,22 +147,2 @@ } | ||
## How it works | ||
Metadata begins with the info objects from the `Loader` of [@gasket/resolve] and | ||
builds data objects for [plugins][PluginData], and [presets][PresetData], and | ||
supporting [modules][ModuleData]. Any functions preset will be **redacted**, as | ||
metadata is not intended to be executed, but rather to is made available to read | ||
and inform plugins. This data can be added to, by hooking the [metadata] | ||
lifecycle in a plugin. | ||
Metadata provides insights to a plugin's shape and package information. | ||
Additional [detail info][DetailData] of plugins can added in the [metadata] | ||
lifecycle, such as what commands, lifecycles, or structures, a plugin provides. | ||
The [metadata object] be accessed in lifecycle hooks from `gasket.metadata`. | ||
Additionally, [ModuleData] for all the top-level app's dependencies are loaded | ||
by default, and is available from `gasket.metadata.app.modules`. Plugins can | ||
choose to bring in metadata for more modules, or augment what has already been | ||
loaded for the app. These, along with the app's modules, will be flattened and | ||
available from `gasket.metadata.modules`. | ||
## License | ||
@@ -196,18 +154,6 @@ | ||
[metadata]: #metadata | ||
[ModuleData]: docs/api.md#ModuleData | ||
[PluginData]: docs/api.md#PluginData | ||
[PresetData]: docs/api.md#PresetData | ||
[DetailData]: docs/api.md#DetailData | ||
[metadata object]: docs/api.md#DetailData | ||
[@gasket/plugin-docs]: /packages/gasket-plugin-docs/README.md | ||
[@gasket/resolve]: /packages/gasket-resolve/README.md | ||
[init lifecycle]: /packages/gasket-plugin-command/README.md#init |
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
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
9
340
0
Yes
17425
2
152
10
+ Added@gasket/plugin-logger@^7.2.0
+ Added@gasket/plugin-logger@7.2.1(transitive)
Updated@gasket/core@^7.2.0