@gasket/plugin-webpack
Advanced tools
+1
-11
@@ -9,4 +9,3 @@ /// <reference types="create-gasket-app" /> | ||
| version, | ||
| description, | ||
| devDependencies | ||
| description | ||
| } = packageJson; | ||
@@ -21,11 +20,2 @@ | ||
| hooks: { | ||
| create(gasket, { pkg, gasketConfig }) { | ||
| gasketConfig.addPlugin('pluginWebpack', name); | ||
| pkg.add('dependencies', { | ||
| [name]: `^${version}` | ||
| }); | ||
| pkg.add('devDependencies', { | ||
| webpack: devDependencies.webpack | ||
| }); | ||
| }, | ||
| metadata(gasket, meta) { | ||
@@ -32,0 +22,0 @@ return { |
+6
-12
| { | ||
| "name": "@gasket/plugin-webpack", | ||
| "version": "7.4.0", | ||
| "version": "8.0.0-next.1", | ||
| "description": "Adds webpack support to your application", | ||
| "type": "module", | ||
| "main": "lib/index.js", | ||
| "types": "lib/index.d.ts", | ||
@@ -11,3 +10,2 @@ "files": [ | ||
| "lib", | ||
| "cjs", | ||
| "EXAMPLES.md" | ||
@@ -18,5 +16,3 @@ ], | ||
| "types": "./lib/index.d.ts", | ||
| "import": "./lib/index.js", | ||
| "require": "./cjs/index.cjs", | ||
| "default": "./cjs/index.cjs" | ||
| "import": "./lib/index.js" | ||
| }, | ||
@@ -41,10 +37,8 @@ "./package.json": "./package.json" | ||
| "webpack": "^5.98.0", | ||
| "@gasket/cjs": "^7.1.0", | ||
| "@gasket/core": "^7.6.3", | ||
| "@gasket/plugin-logger": "^7.4.0", | ||
| "@gasket/plugin-metadata": "^7.5.5", | ||
| "create-gasket-app": "^7.4.15" | ||
| "@gasket/core": "^8.0.0-next.1", | ||
| "@gasket/plugin-logger": "^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 ./lib", | ||
| "lint": "eslint .", | ||
@@ -51,0 +45,0 @@ "lint:fix": "pnpm run lint --fix", |
| /// <reference types="@gasket/plugin-logger" /> | ||
| 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, { | ||
| default: function() { | ||
| return _default; | ||
| }, | ||
| getWebpackConfig: function() { | ||
| return getWebpackConfig; | ||
| } | ||
| }); | ||
| const _module = require("module"); | ||
| const _webpackmetricsplugin = /*#__PURE__*/ _interop_require_default(require("./webpack-metrics-plugin.cjs")); | ||
| const _gasketenvguardplugin = /*#__PURE__*/ _interop_require_default(require("./gasket-env-guard-plugin.cjs")); | ||
| function _interop_require_default(obj) { | ||
| return obj && obj.__esModule ? obj : { | ||
| default: obj | ||
| }; | ||
| } | ||
| const require1 = (0, _module.createRequire)(require("url").pathToFileURL(__filename).toString()); | ||
| /** | ||
| * Sets up a context object with special getters | ||
| * @type {import('./internal.d.ts').setupContext} | ||
| */ function setupContext(context) { | ||
| return { | ||
| ...context, | ||
| get webpack () { | ||
| const webpack = require1('webpack'); | ||
| return webpack; | ||
| } | ||
| }; | ||
| } | ||
| function getWebpackConfig(gasket, initConfig, context) { | ||
| var _baseConfig, _baseConfig_resolve; | ||
| /** @type {import('webpack').Configuration} */ const baseConfig = { | ||
| ...initConfig, | ||
| plugins: [ | ||
| ...initConfig && initConfig.plugins ? initConfig.plugins : [], | ||
| new _webpackmetricsplugin.default({ | ||
| gasket | ||
| }), | ||
| new _gasketenvguardplugin.default() | ||
| ].filter(Boolean) | ||
| }; | ||
| (_baseConfig = baseConfig).resolve ?? (_baseConfig.resolve = {}); | ||
| (_baseConfig_resolve = baseConfig.resolve).alias ?? (_baseConfig_resolve.alias = {}); | ||
| const alias = /** @type {Record<string, string | false>} */ baseConfig.resolve.alias; | ||
| alias.webpack = false; | ||
| baseConfig.resolve.alias = alias; | ||
| return gasket.execWaterfallSync('webpackConfig', baseConfig, setupContext(context)); | ||
| } | ||
| const _default = { | ||
| getWebpackConfig | ||
| }; |
| /** | ||
| * Webpack plugin that prevents process.env.GASKET_ENV from being bundled | ||
| * in browser code to avoid leaking server-side configuration. | ||
| */ Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| Object.defineProperty(exports, "default", { | ||
| enumerable: true, | ||
| get: function() { | ||
| return _default; | ||
| } | ||
| }); | ||
| class GasketEnvGuardPlugin { | ||
| // No constructor needed - this plugin requires no configuration | ||
| apply(compiler) { | ||
| const pluginName = 'GasketEnvGuardPlugin'; | ||
| // Hook into the compilation to scan modules | ||
| compiler.hooks.compilation.tap(pluginName, (compilation)=>{ | ||
| // Hook into the module build process | ||
| compilation.hooks.buildModule.tap(pluginName, (module)=>{ | ||
| // Skip node_modules | ||
| if (module.resource && module.resource.includes('node_modules')) { | ||
| return; | ||
| } | ||
| // Check for GASKET_ENV usage in the module source | ||
| if (module.resource && this.shouldCheckModule(module.resource)) { | ||
| this.scheduleSourceCheck(compilation, module); | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
| shouldCheckModule(resourcePath) { | ||
| // Only check JavaScript/TypeScript files that are not in node_modules | ||
| const jsExtensions = [ | ||
| '.js', | ||
| '.jsx', | ||
| '.ts', | ||
| '.tsx', | ||
| '.mjs' | ||
| ]; | ||
| return jsExtensions.some((ext)=>resourcePath.endsWith(ext)) && !resourcePath.includes('node_modules'); | ||
| } | ||
| scheduleSourceCheck(compilation, module) { | ||
| // Hook into after module build to check the source | ||
| compilation.hooks.succeedModule.tap('GasketEnvGuardPlugin', (builtModule)=>{ | ||
| if (builtModule === module && builtModule._source) { | ||
| this.checkModuleSource(compilation, builtModule); | ||
| } | ||
| }); | ||
| } | ||
| checkModuleSource(compilation, module) { | ||
| let source = ''; | ||
| // Try to get the source code | ||
| if (module._source && module._source.source) { | ||
| source = module._source.source(); | ||
| } else if (module.originalSource && module.originalSource.source) { | ||
| source = module.originalSource.source(); | ||
| } | ||
| if (source && this.containsGasketEnv(source)) { | ||
| this.handleGasketEnvDetection(compilation, module.resource); | ||
| } | ||
| } | ||
| containsGasketEnv(source) { | ||
| // Remove comments but preserve string patterns that are actual code | ||
| const cleanedSource = source// Remove single-line comments | ||
| .replace(/\/\/.*$/gm, '')// Remove multi-line comments | ||
| .replace(/\/\*[\s\S]*?\*\//g, ''); | ||
| // Check for various patterns of GASKET_ENV usage | ||
| const patterns = [ | ||
| /process\.env\.GASKET_ENV/g, | ||
| /process\.env\['GASKET_ENV'\]/g, | ||
| /process\.env\["GASKET_ENV"\]/g, | ||
| /process\.env\[`GASKET_ENV`\]/g | ||
| ]; | ||
| // Check if any pattern matches, but avoid matching inside string literals | ||
| for (const pattern of patterns){ | ||
| const matches = [ | ||
| ...cleanedSource.matchAll(pattern) | ||
| ]; | ||
| for (const match of matches){ | ||
| const beforeMatch = cleanedSource.substring(0, match.index); | ||
| // Simple check: count unescaped quotes before the match | ||
| // If odd number of quotes, we're likely inside a string | ||
| const singleQuotes = (beforeMatch.match(RegExp("(?<!\\\\)'", "g")) || []).length; | ||
| const doubleQuotes = (beforeMatch.match(RegExp('(?<!\\\\)"', "g")) || []).length; | ||
| const backticks = (beforeMatch.match(RegExp("(?<!\\\\)`", "g")) || []).length; | ||
| // If we're not inside quotes, this is a real match | ||
| if (singleQuotes % 2 === 0 && doubleQuotes % 2 === 0 && backticks % 2 === 0) { | ||
| return true; | ||
| } | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
| handleGasketEnvDetection(compilation, resourcePath) { | ||
| const message = resourcePath ? `process.env.GASKET_ENV detected in ${resourcePath}` : 'process.env.GASKET_ENV detected in browser bundle'; | ||
| const error = new Error(`${message}!\n\n` + `GASKET_ENV is intended for server-side environment configuration only.\n` + `Including it in browser bundles can expose sensitive configuration.\n\n` + `Recommended solutions:\n` + `1. Use gasket.config.env for environment-specific config\n` + `2. Use @gasket/data to pass server data to the client\n` + `3. Move environment-specific logic to server-side code\n\n` + `For more guidance, see: https://gasket.dev/docs/guides/webpack#gasket-env-protection`); | ||
| compilation.errors.push(error); | ||
| } | ||
| } | ||
| const _default = GasketEnvGuardPlugin; |
| /// <reference types="create-gasket-app" /> | ||
| /// <reference types="@gasket/plugin-metadata" /> | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| Object.defineProperty(exports, "default", { | ||
| enumerable: true, | ||
| get: function() { | ||
| return _default; | ||
| } | ||
| }); | ||
| const _actions = /*#__PURE__*/ _interop_require_default(require("./actions.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, devDependencies } = _packagejson.default; | ||
| /** @type {import('@gasket/core').Plugin} */ const plugin = { | ||
| name, | ||
| version, | ||
| description, | ||
| actions: _actions.default, | ||
| hooks: { | ||
| create (gasket, { pkg, gasketConfig }) { | ||
| gasketConfig.addPlugin('pluginWebpack', name); | ||
| pkg.add('dependencies', { | ||
| [name]: `^${version}` | ||
| }); | ||
| pkg.add('devDependencies', { | ||
| webpack: devDependencies.webpack | ||
| }); | ||
| }, | ||
| metadata (gasket, meta) { | ||
| return { | ||
| ...meta, | ||
| actions: [ | ||
| { | ||
| name: 'getWebpackConfig', | ||
| description: 'Get the webpack config', | ||
| link: 'README.md#getWebpackConfig' | ||
| } | ||
| ], | ||
| guides: [ | ||
| { | ||
| name: 'Webpack Configuration Guide', | ||
| description: 'Configuring Webpack in Gasket apps', | ||
| link: 'docs/webpack.md' | ||
| } | ||
| ], | ||
| lifecycles: [ | ||
| { | ||
| name: 'webpackConfig', | ||
| method: 'execApplySync', | ||
| description: 'Transform the Webpack config', | ||
| link: 'README.md#webpackConfig', | ||
| parent: 'initWebpack', | ||
| after: 'webpack' | ||
| }, | ||
| { | ||
| name: 'initWebpack', | ||
| description: 'Create a webpack config', | ||
| command: 'build', | ||
| link: 'README.md#initwebpack' | ||
| } | ||
| ] | ||
| }; | ||
| } | ||
| } | ||
| }; | ||
| const _default = plugin; |
| {} |
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| Object.defineProperty(exports, "default", { | ||
| enumerable: true, | ||
| get: function() { | ||
| return _default; | ||
| } | ||
| }); | ||
| const _path = /*#__PURE__*/ _interop_require_default(require("path")); | ||
| const _module = require("module"); | ||
| function _interop_require_default(obj) { | ||
| return obj && obj.__esModule ? obj : { | ||
| default: obj | ||
| }; | ||
| } | ||
| const require1 = (0, _module.createRequire)(require("url").pathToFileURL(__filename).toString()); | ||
| /** | ||
| * This is a webpack plugin for gathering bundle size data | ||
| */ class WebpackMetricsPlugin { | ||
| /** | ||
| * Helper function to call the metrics lifecycle | ||
| * @type {import('./internal.d.ts').handleMetrics} | ||
| */ async handleMetrics(metrics) { | ||
| this.gasket.logger.debug('webpack metrics: ' + JSON.stringify(metrics, null, 2)); | ||
| } | ||
| /** | ||
| * This plugin will calculate the sizes of the directories from the webpack | ||
| * bundle sent to the browser and call the metrics lifecycle with the data. | ||
| * | ||
| * Example format of data to emit: | ||
| * | ||
| * { name: '@gasket/canary-app', | ||
| * event: 'webpack', | ||
| * data: { | ||
| * images: { totalSize: 101231, jpg: 101231 }, | ||
| * chunks: { totalSize: 128770, js: 128770 }, | ||
| * runtime: { totalSize: 17671, js: 17671 }, | ||
| * css: { totalSize: 749, css: 749 }, | ||
| * pages: { totalSize: 782744, js: 782744 }, | ||
| * 'bundle.svgs': { totalSize: 10188, svgs: 10188 } }, | ||
| * time: 1559323660583 } | ||
| * @type {import('./internal.d.ts').apply} | ||
| */ apply(compiler) { | ||
| const { target, context } = compiler.options; | ||
| if (target !== 'web') return; | ||
| // eslint-disable-next-line max-statements | ||
| compiler.hooks.emit.tap('WebpackMetricsPlugin', ({ assets })=>{ | ||
| const map = {}; | ||
| let name; | ||
| try { | ||
| const packageJSON = require1(`${context}/package.json`); | ||
| name = packageJSON.name; | ||
| } catch { | ||
| name = 'Gasket App'; | ||
| } | ||
| for (const fullpath of Object.keys(assets)){ | ||
| const asset = assets[fullpath]; | ||
| const fileSize = asset.size(); | ||
| const parsed = _path.default.parse(fullpath); | ||
| const extension = parsed.ext.slice(1); | ||
| let dirname = _path.default.basename(parsed.dir); | ||
| const parent = _path.default.basename(_path.default.dirname(parsed.dir)); | ||
| if (parent === 'pages') { | ||
| dirname = parent; | ||
| } | ||
| map[dirname] = map[dirname] || { | ||
| totalSize: 0 | ||
| }; | ||
| map[dirname].totalSize += fileSize; | ||
| map[dirname][extension] = map[dirname][extension] || 0; | ||
| map[dirname][extension] += fileSize; | ||
| } | ||
| /** @type {import('./index.js').WebpackMetrics} */ const metrics = { | ||
| name, | ||
| event: 'webpack', | ||
| data: map, | ||
| time: Date.now() | ||
| }; | ||
| // logging these errors more obvious to future plugin authors. | ||
| this.handleMetrics(metrics).catch(()=>{}); | ||
| }); | ||
| } | ||
| constructor(opts){ | ||
| /** @type {import('@gasket/core').Gasket} */ this.gasket = opts.gasket; | ||
| } | ||
| } | ||
| const _default = WebpackMetricsPlugin; |
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
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
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
6
-14.29%7
-41.67%30445
-30.07%12
-29.41%322
-50.84%1
Infinity%