UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

312 lines (281 loc) 8.32 kB
/* ************************************************************************ * * qooxdoo-compiler - node.js based replacement for the Qooxdoo python * toolchain * * https://github.com/qooxdoo/qooxdoo-compiler * * Copyright: * 2011-2021 Zenesis Limited, http://www.zenesis.com * * License: * MIT: https://opensource.org/licenses/MIT * * This software is provided under the same licensing terms as Qooxdoo, * please see the LICENSE file in the Qooxdoo project's top-level directory * for details. * * Authors: * * John Spackman (john.spackman@zenesis.com, @johnspackman) * * ************************************************************************/ const path = require("upath"); const fs = require("fs"); /** * A Package is a collection of files and resources, used by either the boot process * or by one or more Parts */ qx.Class.define("qx.tool.compiler.targets.meta.Package", { extend: qx.core.Object, /** * Constructor */ construct(appMeta, packageIndex) { super(); this.__appMeta = appMeta; this.__packageIndex = packageIndex; this.__assets = []; this.__locales = {}; this.__translations = {}; this.__javascriptMetas = []; this.__classnames = []; this.__javascript = new qx.tool.compiler.targets.meta.PackageJavascript( this.__appMeta, this ); }, properties: { /** Whether to embed all the javascript into the one, main package .js file */ embedAllJavascript: { init: false, check: "Boolean" }, /** If true, this is generated on the fly and needs to be output */ needsWriteToDisk: { init: true, check: "Boolean", apply: "_applyNeedsWriteToDisk" } }, members: { /** @type {AppMeta} the AppMeta instance */ __appMeta: null, /** @type {Integer} the package index, 0 == boot package */ __packageIndex: -1, /** @type {qx.tool.compiler.resources.Asset[]} assets to be included in this package */ __assets: null, /** @type {Map} locale data, indexed by locale ID */ __locales: null, /** @type {Map} translations, indexed by message ID */ __translations: null, /** @type {String[]} array of class names loaded by this package */ __classnames: null, /** @type {AbstractJavascriptMeta[]} array of Javascript sources loaded by this package */ __javascriptMetas: null, /** @type {AbstractJavascriptMeta} the javascript generated by this package */ __javascript: null, /** * Detects whether this package is empty; packages can be added for a number * of reasons, but sometimes they don't actually end up with anything in them. * * Note that this is used to suppress the generation of an additional `package-*.js` * file in the output, and just means that the content of the file should be embedded * (or ignored) instead of written into that package file; however, there can still * be script files which need to be loaded by this package (and that is handled by * the index.js file) * * @return {Boolean} */ isEmpty() { if (this.__assets.length > 0) { return false; } for (let localeId in this.__locales) { if (this.__locales[localeId]) { return false; } } for (let localeId in this.__translations) { if (this.__translations[localeId]) { return false; } } if (this.isEmbedAllJavascript()) { if (this.__javascriptMetas.length > 0) { return false; } } return true; }, /** * Returns the package index * * @return {Integer} */ getPackageIndex() { return this.__packageIndex; }, /** * Adds an asset, expected to be unique * * @param asset {qx.tool.compiler.resources.Asset} */ addAsset(asset) { this.__assets.push(asset); }, /** * Returns the array of assets * * @return {qx.tool.compiler.resources.Asset[]} */ getAssets() { return this.__assets; }, /** * Adds locale data * * @param localeId {String} * @param localeData {Object} */ addLocale(localeId, localeData) { this.__locales[localeId] = localeData; }, /** * Returns locale data, as a map where the key is the locale ID * * @return {Map} */ getLocales() { return this.__locales; }, /** * Adds a translation * * @param localeId {String} locale ID * @param entry {Object} translation */ addTranslationEntry(localeId, entry) { let translations = this.__translations[localeId]; if (!translations) { this.__translations[localeId] = translations = {}; } var msgstr = entry.msgstr; if (!qx.lang.Type.isArray(msgstr)) { msgstr = [msgstr]; } if (msgstr[0]) { translations[entry.msgid] = msgstr[0]; } if (entry.msgid_plural && msgstr[1]) { translations[entry.msgid_plural] = msgstr[1]; } }, /** * Returns a map of all translations, indexed by Locale ID * * @return {Object} */ getTranslations() { return this.__translations; }, /** * Adds a Javascript to be loaded by this package. You typically need to * call `addClassname` also. * * @param jsMeta {AbstractJavascriptMeta} */ addJavascriptMeta(jsMeta) { this.__javascriptMetas.push(jsMeta); }, /** * Returns a list of all Javascripts to be loaded by this package * * @return {AbstractJavascriptMeta[]} */ getJavascriptMetas() { return this.__javascriptMetas; }, /** * Removes a Javascript * * @param jsMeta {AbstractJavascriptMeta} the javascript to remove */ removeJavascriptMeta(jsMeta) { qx.lang.Array.remove(this.__javascriptMetas, jsMeta); }, /** * Adds a classname to the list which is loaded by this package; this does not * cause the code to be loaded, @see {addJavascriptMeta}. * * @param classname {String} */ addClassname(classname) { this.__classnames.push(classname); }, /** * Returns a list of all classnames * * @return {String[]} */ getClassnames() { return this.__classnames; }, /** * Returns the AbstractJavascriptMeta for this Package * * @return {AbstractJavascriptMeta} */ getJavascript() { return this.__javascript; }, /** * Writes the data into the configuration which is passed to the loader template * * @param packages {Object} the `qx.$$packages` object data */ serializeInto(packages) { let data = (packages[String(this.__packageIndex)] = { uris: [] }); let appRoot = this.__appMeta.getApplicationRoot(); let target = this.__appMeta.getTarget(); let privateArtifacts = target.isPrivateArtifacts() && this.__appMeta.getApplication().getType() == "browser"; let transpiledDir = path.join(target.getOutputDir(), "transpiled"); let resourceDir = path.join(target.getOutputDir(), "resource"); const toUri = filename => { if ( privateArtifacts && (filename.startsWith(transpiledDir) || filename.startsWith(resourceDir)) ) { let uri = path.relative(target.getOutputDir(), filename); return uri; } let uri = path.relative(appRoot, filename); if (this.__appMeta.isAddTimestampsToUrls()) { let stat = fs.statSync(filename, { throwIfNoEntry: false }); if (stat) { uri += "?" + stat.mtimeMs; } return uri; } else { return uri; } }; if (!this.isEmbedAllJavascript()) { data.uris = this.__javascriptMetas.map(js => toUri(js.getFilename())); } if (this.isNeedsWriteToDisk()) { data.uris.unshift(toUri(this.__javascript.getFilename())); } }, /** * Apply for needsWriteToDisk property */ _applyNeedsWriteToDisk(value) { this.__javascript.setNeedsWriteToDisk(value); } } });