/
githubmirror
/
meteor
Обзор
Документация
Войти
/
githubmirror
/
meteor
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
devel
tools/isobuild/build-plugin.js
523 строки
17 KB
Matheus Castro
Remove Fibers for meteor tools: turns out the package building is still not done. Still need to check why it's failing to require node_modules when evaluating/loading the plugin.
04 дек 2022, 06:27
04 дек 2022, 06:27
b1a6cc0
Код
Авторство
О чём код?
var archinfo = require('../utils/archinfo'); var buildmessage = require('../utils/buildmessage.js'); var files = require('../fs/files'); var _ = require('underscore'); import utils from '../utils/utils.js'; let nextId = 1; exports.SourceProcessor = function (options) { var self = this; self.isopack = options.isopack; self.extensions = (options.extensions || []).slice(); self.filenames = (options.filenames || []).slice(); self.archMatching = options.archMatching; self.isTemplate = !! options.isTemplate; self.factoryFunction = options.factoryFunction; self.methodName = options.methodName; self.id = `${ options.isopack.displayName() }#${ nextId++ }`; self.userPlugin = null; }; Object.assign(exports.SourceProcessor.prototype, { // Call the user's factory function to get the actual build plugin object. // Note that we're supposed to have one userPlugin per project, so this // assumes that each Isopack object is specific to a project. We don't run // this immediately on evaluating Plugin.registerCompiler; we instead wait // until the whole plugin file has been evaluated (so that it can use things // defined later in the file). instantiatePlugin: async function () { var self = this; buildmessage.assertInCapture(); if (self.userPlugin) { throw Error("Called instantiatePlugin twice?"); } await buildmessage.enterJob( `running ${self.methodName} callback in package ` + self.isopack.displayName(), async () => { try { const markedFactoryFunction = buildmessage.markBoundary(self.factoryFunction); self.userPlugin = await markedFactoryFunction.call(null); // If we have a disk cache directory and the plugin wants it, use it. if (self.isopack.pluginCacheDir && self.userPlugin.setDiskCacheDirectory) { await buildmessage.markBoundary(function () { self.userPlugin.setDiskCacheDirectory( files.convertToOSPath(self.isopack.pluginCacheDir) ); })(); } } catch (e) { buildmessage.exception(e); } } ); }, relevantForArch: function (arch) { var self = this; return ! self.archMatching || archinfo.matches(arch, self.archMatching); } }); // Represents a set of SourceProcessors available in a given package. They may // not have conflicting extensions or filenames. export class SourceProcessorSet { constructor(myPackageDisplayName, { hardcodeJs, singlePackage, allowConflicts, } = {}) { // For error messages only. this._myPackageDisplayName = myPackageDisplayName; // If this represents the SourceProcessors *registered* by a single package // (vs those *available* to a package), use different error messages. this._singlePackage = singlePackage; // If this is being used for *compilers*, we hardcode *.js. If it is being // used for linters, we don't. this._hardcodeJs = !! hardcodeJs; // Multiple linters may be registered on the same extension or filename, but // not compilers. this._allowConflicts = !! allowConflicts; // Map from extension -> [SourceProcessor] this._byExtension = {}; // Map from basename -> [SourceProcessor] this._byFilename = {}; // This is just an duplicate-free list of all SourceProcessors in // byExtension or byFilename. this.allSourceProcessors = []; // extension -> { handler, packageDisplayName, isTemplate, archMatching } this._legacyHandlers = {}; } _conflictError(package1, package2, conflict) { if (this._singlePackage) { buildmessage.error( `plugins in package ${ this._myPackageDisplayName } define multiple ` + `handlers for ${ conflict }`); } else { buildmessage.error( `conflict: two packages included in ${ this._myPackageDisplayName } ` + `(${ package1 } and ${ package2 }) are both trying to handle ` + conflict); } } addSourceProcessor(sp) { buildmessage.assertInJob(); this._addSourceProcessorHelper(sp, sp.extensions, this._byExtension, '*.'); this._addSourceProcessorHelper(sp, sp.filenames, this._byFilename, ''); // If everything conflicted, then the SourceProcessors will be in // allSourceProcessors but not any of the data structures, but in that case // the caller should be checking for errors anyway. this.allSourceProcessors.push(sp); } _addSourceProcessorHelper(sp, things, byThing, errorPrefix) { buildmessage.assertInJob(); things.forEach((thing) => { if (byThing.hasOwnProperty(thing)) { if (this._allowConflicts) { byThing[thing].push(sp); } else { this._conflictError(sp.isopack.displayName(), byThing[thing][0].isopack.displayName(), errorPrefix + thing); // recover by ignoring this one } } else { byThing[thing] = [sp]; } }); } addLegacyHandler({ extension, handler, packageDisplayName, isTemplate, archMatching }) { if (this._allowConflicts) { throw Error("linters have no legacy handlers"); } if (this._byExtension.hasOwnProperty(extension)) { this._conflictError(packageDisplayName, this._byExtension[extension].isopack.displayName(), '*.' + extension); // recover by ignoring return; } if (this._legacyHandlers.hasOwnProperty(extension)) { this._conflictError(packageDisplayName, this._legacyHandlers[extension].packageDisplayName, '*.' + extension); // recover by ignoring return; } this._legacyHandlers[extension] = {handler, packageDisplayName, isTemplate, archMatching}; } // Adds all the source processors (and legacy handlers) from the other set to // this one. Logs buildmessage errors on conflict. Ignores packageDisplayName // and singlePackage. If arch is set, skips SourceProcessors that // don't match it. merge(otherSet, options = {}) { const { arch } = options; buildmessage.assertInJob(); otherSet.allSourceProcessors.forEach((sourceProcessor) => { if (! arch || sourceProcessor.relevantForArch(arch)) { this.addSourceProcessor(sourceProcessor); } }); _.each(otherSet._legacyHandlers, (info, extension) => { const { handler, packageDisplayName, isTemplate, archMatching } = info; this.addLegacyHandler( {extension, handler, packageDisplayName, isTemplate, archMatching}); }); } // Note: Only returns SourceProcessors, not legacy handlers. getByExtension(extension) { if (this._allowConflicts) { throw Error("Can't call getByExtension for linters"); } if (this._byExtension.hasOwnProperty(extension)) { return this._byExtension[extension][0]; } return null; } // Note: Only returns SourceProcessors, not legacy handlers. getByFilename(filename) { if (this._allowConflicts) { throw Error("Can't call getByFilename for linters"); } if (this._byFilename.hasOwnProperty(filename)) { return this._byFilename[filename][0]; } return null; } // filename, arch -> SourceClassification classifyFilename(filename, arch) { // First check to see if a plugin registered for this exact filename. if (this._byFilename.hasOwnProperty(filename)) { return new SourceClassification('filename', { arch, sourceProcessors: this._byFilename[filename].slice() }); } if (filename === ".meteorignore") { return new SourceClassification("meteor-ignore"); } // Now check to see if a plugin registered for an extension. We prefer // longer extensions. const parts = filename.split('.'); // don't use iteration functions, so we can return (and start at #1) for (let i = 1; i < parts.length; i++) { const extension = parts.slice(i).join('.'); if (this._byExtension.hasOwnProperty(extension)) { return new SourceClassification('extension', { arch, extension, sourceProcessors: this._byExtension[extension] }); } if (this._hardcodeJs && extension === 'js') { // If there is no special sourceProcessor for handling a .js file, // we can still classify it as extension/js, only without any // source processors. #HardcodeJs return new SourceClassification('extension', { extension, usesDefaultSourceProcessor: true }); } if (this._legacyHandlers.hasOwnProperty(extension)) { const legacy = this._legacyHandlers[extension]; if (legacy.archMatching && ! archinfo.matches(arch, legacy.archMatching)) { return new SourceClassification('wrong-arch'); } return new SourceClassification('legacy-handler', { extension, legacyHandler: legacy.handler, legacyIsTemplate: legacy.isTemplate }); } } // Nothing matches; it must be a static asset (or a non-linted file). return new SourceClassification('unmatched'); } isEmpty() { return _.isEmpty(this._byFilename) && _.isEmpty(this._byExtension) && _.isEmpty(this._legacyHandlers); } isConflictsAllowed() { return this._allowConflicts; } // Returns an options object suitable for passing to // `watch.readAndWatchDirectory` to find source files processed by this // SourceProcessorSet. appReadDirectoryOptions(arch) { const include = []; const names = []; let addedJs = false; function addExtension(ext) { include.push(new RegExp('\\.' + utils.quotemeta(ext) + '$')); if (ext === 'js') { addedJs = true; } } _.each(this._byExtension, (sourceProcessors, ext) => { if (sourceProcessors.some(sp => sp.relevantForArch(arch))) { addExtension(ext); } }); Object.keys(this._legacyHandlers).forEach(addExtension); if (this._hardcodeJs && ! addedJs) { // If there is no sourceProcessor for handling .js files, we still // want to make sure they get picked up when we're reading the // contents of app directories. #HardcodeJs addExtension('js'); } _.each(this._byFilename, (sourceProcessors, filename) => { if (sourceProcessors.some(sp => sp.relevantForArch(arch))) { names.push(filename); } }); return {include, names, exclude: []}; } } class SourceClassification { constructor(type, { legacyHandler, extension, sourceProcessors, usesDefaultSourceProcessor, legacyIsTemplate, arch, } = {}) { const knownTypes = [ 'extension', 'filename', 'legacy-handler', 'wrong-arch', 'unmatched', 'meteor-ignore', ]; if (knownTypes.indexOf(type) === -1) { throw Error(`Unknown SourceClassification type ${ type }`); } // This is the only thing we can write to `this` before checking for // wrong-arch. this.type = type; if (type === 'extension' || type === 'filename') { if (sourceProcessors) { if (! arch) { throw Error("need to filter based on arch!"); } // If there's a SourceProcessor (or legacy handler) registered for this // file but not for this arch, we want to ignore it instead of // processing it or treating it as a static asset. (Note that prior to // the batch-plugins project, files added in a package with // `api.addFiles('foo.bar')` where *.bar is a web-specific legacy // handler (eg) would end up adding 'foo.bar' as a static asset on // non-web programs, which was unintended. This didn't happen in apps // because initFromAppDir's getFiles never added them.) const filteredSourceProcessors = sourceProcessors.filter( (sourceProcessor) => sourceProcessor.relevantForArch(arch) ); if (! filteredSourceProcessors.length) { // Wrong architecture! Rewrite this.type and return. (Note that we // haven't written anything else to `this` so far.) this.type = 'wrong-arch'; return; } this.sourceProcessors = filteredSourceProcessors; } else if (!(type === 'extension' && extension === 'js')) { // 'extension' and 'filename' classifications need to have at least one // SourceProcessor, unless it's the #HardcodeJs special case. throw Error(`missing sourceProcessors for ${ type }!`); } } if (type === 'legacy-handler') { if (! legacyHandler) { throw Error('SourceClassification needs legacyHandler!'); } if (legacyIsTemplate === undefined) { throw Error('SourceClassification needs legacyIsTemplate!'); } this.legacyHandler = legacyHandler; this.legacyIsTemplate = legacyIsTemplate; } if (type === 'extension' || type === 'legacy-handler') { if (! extension) { throw Error('extension SourceClassification needs extension!'); } this.extension = extension; } if (usesDefaultSourceProcessor) { if (this.extension !== 'js' && this.extension !== 'css') { // We only currently hard-code support for processing .js files // when no source processor is registered (#HardcodeJs). Default // support could conceivably be extended to .css files too, but // anything else is almost certainly a mistake. throw Error('non-JS/CSS file relying on default source processor?'); } this.usesDefaultSourceProcessor = true; } else { this.usesDefaultSourceProcessor = false; } } isNonLegacySource() { return this.type === 'extension' || this.type === 'filename'; } } // This is the base class of the object presented to the user's plugin code. export class InputFile { /** * @summary Returns the full contents of the file as a buffer. * @memberof InputFile * @returns {Buffer} */ getContentsAsBuffer() { throw new Error("Not Implemented"); } /** * @summary Returns the name of the package or `null` if the file is not in a * package. * @memberof InputFile * @returns {String} */ getPackageName() { throw new Error("Not Implemented"); } /** * @summary Returns the relative path of file to the package or app root * directory. The returned path always uses forward slashes. * @memberof InputFile * @returns {String} */ getPathInPackage() { throw new Error("Not Implemented"); } /** * @summary Returns a hash string for the file that can be used to implement * caching. * @memberof InputFile * @returns {String} */ getSourceHash() { throw new Error("Not Implemented"); } /** * @summary Returns the architecture that is targeted while processing this * file. * @memberof InputFile * @returns {String} */ getArch() { throw new Error("Not Implemented"); } /** * @summary Returns the full contents of the file as a string. * @memberof InputFile * @returns {String} */ getContentsAsString() { var self = this; return self.getContentsAsBuffer().toString('utf8'); } /** * @summary Returns the filename of the file. * @memberof InputFile * @returns {String} */ getBasename() { var self = this; return files.pathBasename(self.getPathInPackage()); } /** * @summary Returns the directory path relative to the package or app root. * The returned path always uses forward slashes. * @memberof InputFile * @returns {String} */ getDirname() { var self = this; return files.pathDirname(self.getPathInPackage()); } /** * @summary Returns an object of file options such as those passed as the * third argument to api.addFiles. * @memberof InputFile * @returns {Object} */ getFileOptions() { throw new Error("Not Implemented"); } /** * @summary Call this method to raise a compilation or linting error for the * file. * @param {Object} options * @param {String} options.message The error message to display. * @param {String} [options.sourcePath] The path to display in the error message. * @param {Integer} options.line The line number to display in the error message. * @param {String} options.func The function name to display in the error message. * @memberof InputFile */ error(options) { var self = this; var path = self.getPathInPackage(); var packageName = self.getPackageName(); if (packageName) { path = "packages/" + packageName + "/" + path; } self._reportError(options.message || ("error building " + path), { file: options.sourcePath || path, line: options.line ? options.line : undefined, column: options.column ? options.column : undefined, func: options.func ? options.func : undefined }); } // Default implementation. May be overridden by subclasses. _reportError(message, info) { buildmessage.error(message, info); } }