@metamask/snaps-browserify-plugin
Version:
A Browserify plugin to build MetaMask Snaps with Browserify
1 lines • 9.16 kB
Source Map (JSON)
{"version":3,"file":"plugin.cjs","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PostProcessOptions } from '@metamask/snaps-utils/node';\nimport {\n checkManifest,\n evalBundle,\n logWarning,\n postProcessBundle,\n useTemporaryFile,\n} from '@metamask/snaps-utils/node';\nimport type { BrowserifyObject } from 'browserify';\nimport { fromSource } from 'convert-source-map';\nimport pathUtils from 'path';\nimport type { TransformCallback } from 'readable-stream';\nimport { Transform } from 'readable-stream';\n\ntype PluginOptions = {\n eval?: boolean;\n manifestPath?: string;\n writeManifest?: boolean;\n};\n\nexport type Options = PluginOptions &\n Omit<PostProcessOptions, 'sourceMap' | 'inputSourceMap'>;\n\n/**\n * Run eval on the processed bundle and fix the manifest, if configured.\n *\n * @param options - The plugin options.\n * @param code - The code to eval, if the eval option is enabled.\n */\nasync function postBundle(options: Partial<Options>, code: string) {\n if (options.eval) {\n await useTemporaryFile('snaps-bundle.js', code, (path) => evalBundle(path));\n }\n\n if (options.manifestPath) {\n const { reports } = await checkManifest(\n pathUtils.dirname(options.manifestPath),\n {\n sourceCode: code,\n updateAndWriteManifest: options.writeManifest,\n },\n );\n\n const errorsUnfixed = reports\n .filter((report) => report.severity === 'error' && !report.wasFixed)\n .map((report) => report.message);\n const warnings = reports\n .filter((report) => report.severity === 'warning' && !report.wasFixed)\n .map((report) => report.message);\n const fixed = reports\n .filter((report) => report.wasFixed)\n .map((report) => report.message);\n\n if (errorsUnfixed.length > 0) {\n throw new Error(\n `Manifest Error: The manifest is invalid.\\n${errorsUnfixed.join('\\n')}`,\n );\n }\n\n if (warnings.length > 0) {\n logWarning(\n 'Manifest Warning: Validation of snap.manifest.json completed with warnings.',\n );\n\n warnings.forEach((warning) => logWarning(`Manifest Warning: ${warning}`));\n }\n\n if (fixed.length > 0) {\n logWarning(\n `Manifest Warning: Validation of snap.manifest.json fixed following problems.`,\n );\n\n fixed.forEach((error) => logWarning(`Manifest Problem Fixed: ${error}`));\n }\n }\n}\n\n/**\n * A transform stream which can be used in the Browserify pipeline. It accepts a\n * string input, which is post-processed and pushed to the output stream.\n */\nexport class SnapsBrowserifyTransform extends Transform {\n readonly #data: Buffer[] = [];\n\n readonly #options: Partial<Options>;\n\n /**\n * Construct an instance of the transform stream.\n *\n * @param options - The post-processing options.\n * @param options.stripComments - Whether to strip comments. Defaults to `true`.\n * @param options.transformHtmlComments - Whether to transform HTML comments.\n * Defaults to `true`.\n */\n constructor(options: Partial<Options> = {}) {\n super();\n this.#options = { ...options };\n }\n\n /**\n * Takes a chunk of data and pushes it into an internal array, for later\n * processing.\n *\n * @param chunk - The chunk of data to transform.\n * @param _encoding - The encoding of the chunk.\n * @param callback - The callback to call when the chunk is processed.\n */\n _transform(\n chunk: Buffer,\n _encoding: BufferEncoding,\n callback: TransformCallback,\n ) {\n // Collects all the chunks into an array.\n this.#data.push(chunk);\n callback();\n }\n\n /**\n * Takes the internal array of chunks and processes them. The processed code\n * is pushed to the output stream.\n *\n * @param callback - The callback to call when the stream is finished.\n */\n _flush(callback: TransformCallback) {\n // Merges all the chunks into a single string and processes it.\n const code = Buffer.concat(this.#data).toString('utf-8');\n\n // Browserify uses inline source maps, so we attempt to read it here, and\n // convert it to an object.\n const inputSourceMap = fromSource(code)?.toObject() ?? undefined;\n\n const result = postProcessBundle(code, {\n ...this.#options,\n sourceMap: Boolean(inputSourceMap) && 'inline',\n inputSourceMap,\n });\n\n if (result.warnings.length > 0) {\n logWarning(\n `Bundle Warning: Processing of the Snap bundle completed with warnings.\\n${result.warnings.join(\n '\\n',\n )}`,\n );\n }\n\n postBundle(this.#options, result.code)\n .catch((error) => {\n callback(error);\n })\n .finally(() => {\n this.push(result.code);\n callback();\n });\n }\n}\n\n/**\n * The Browserify plugin function. Can be passed to the Browserify `plugin`\n * function, or used by simply passing the package name to `plugin`.\n *\n * @param browserifyInstance - The Browserify instance.\n * @param options - The plugin options.\n * @param options.stripComments - Whether to strip comments. Defaults to `true`.\n * @param options.eval - Whether to evaluate the bundle to test SES\n * compatibility. Defaults to `true`.\n * @param options.manifestPath - The path to the manifest file. If provided,\n * the manifest will be validated. Defaults to\n * `process.cwd() + '/snap.manifest.json'`.\n * @param options.writeManifest - Whether to fix the manifest.\n * Defaults to `true`.\n */\nexport default function plugin(\n browserifyInstance: BrowserifyObject,\n options?: Partial<Options>,\n): void {\n const defaultOptions = {\n eval: true,\n manifestPath: pathUtils.join(process.cwd(), 'snap.manifest.json'),\n writeManifest: true,\n ...options,\n };\n\n // Pushes the transform stream at the end of Browserify's pipeline. This\n // ensures that the transform is run on the entire bundle.\n browserifyInstance.pipeline.push(\n new SnapsBrowserifyTransform(defaultOptions),\n );\n}\n"]}