UNPKG

@enact/dev-utils

Version:

A collection of development utilities for Enact apps.

207 lines (195 loc) 6.79 kB
const fs = require('fs'); const path = require('path'); const glob = require('glob'); // List of asset-pointing appinfo properties. const props = ['icon', 'largeIcon', 'miniicon', 'smallicon', 'splashicon', 'splashBackground', 'bgImage']; // System assets starting with '$' are dynamic and will be within a variable // directory within sysAssetsPath to denote system spec ('HD720', 'HD1080', etc.). let sysAssetsPath = 'sys-assets'; let variableSysPaths = null; // Mapping of absolute asset file paths to their relative distribution output. // This allows us to avoid having multiple of the same files for locales that // share assets. const assetPathCache = {}; function readAppInfo(file) { // Read and parse appinfo.json file if it exists. if (fs.existsSync(file)) { try { const meta = JSON.parse(fs.readFileSync(file, {encoding: 'utf8'})); return meta; } catch (e) { console.log('ERROR: unable to read/parse appinfo.json at ' + file); } } } function handleSysAssetPath(context, appinfo) { // If the sysAsset base is specified, override the default one if (appinfo.sysAssetsBasePath && appinfo.sysAssetsBasePath !== sysAssetsPath) { sysAssetsPath = appinfo.sysAssetsBasePath; variableSysPaths = null; } // As needed, read all the variable irectories for sysAssets const sys = path.join(context, sysAssetsPath); if (!variableSysPaths && fs.existsSync(sys)) { const list = fs.readdirSync(sys); for (let i = 0; i < list.length; i++) { list[i] = path.join(context, sysAssetsPath, list[i]); const stat = fs.statSync(list[i]); if (!stat.isDirectory()) { list.splice(i, 1); i--; } } variableSysPaths = list; } } function detectSysAssets(name) { // find all assets with the name given in the available sysAsset paths const result = []; const trueName = name.substring(1); for (let i = 0; i < variableSysPaths.length; i++) { const abs = path.resolve(path.join(variableSysPaths[i], trueName)); if (fs.existsSync(abs)) { result.push(abs); } } return result; } function rootAppInfo(context, specific) { // The accepted root locations to search for the appinfo.json and its relative // assets are project root or ./webos-meta. const rootDir = [context, path.join(context, './webos-meta')]; // If a specific path is requested, prepend it to the search list if (specific) { if (path.isAbsolute(specific)) { rootDir.unshift(specific); } else { rootDir.unshift(path.join(context, specific)); } } // Check each search location, and if found, return the data and path it was found at. let meta; for (let i = 0; i < rootDir.length; i++) { meta = readAppInfo(path.join(rootDir[i], 'appinfo.json')); if (meta) { return {path: rootDir[i], obj: meta}; } } } function addMetaAssets(metaDir, outDir, appinfo, compilation) { // For each appinfo.json property that contains a webos meta asset, resolve that asset, // and add its data to the compilation assets array. for (let i = 0; i < props.length; i++) { const p = props[i]; if (appinfo[p]) { const assets = appinfo[p].charAt(0) === '$' ? detectSysAssets(appinfo[p]) : [path.resolve(path.join(metaDir, appinfo[p]))]; for (let j = 0; j < assets.length; j++) { const abs = assets[j]; if (appinfo[p].charAt(0) === '$') { if (!assetPathCache[abs]) { assetPathCache[abs] = path.relative(compilation.options.context, abs); } } else if (assetPathCache[abs]) { appinfo[p] = path.relative(outDir, assetPathCache[abs]); } else { assetPathCache[abs] = path.join(outDir, appinfo[p]); } if (!compilation.assets[assetPathCache[abs]]) { try { const data = fs.readFileSync(abs); emitAsset(assetPathCache[abs], compilation.assets, data); } catch (e) { compilation.warnings.push( new Error('WebOSMetaPlugin: Unable to read/emit appinfo asset at ' + abs) ); } } } } } } function emitAsset(name, assets, data) { // Add a given asset's data to the compilation array in a webpack-compatible source object. assets[name] = { size: function() { return data.length; }, source: function() { return data; }, updateHash: function(hash) { return hash.update(data); }, map: function() { return null; } }; } function WebOSMetaPlugin(options) { this.options = options || {}; } module.exports = WebOSMetaPlugin; WebOSMetaPlugin.prototype.apply = function(compiler) { const scan = this.options.path; const context = this.options.context || compiler.options.context; compiler.plugin('emit', (compilation, callback) => { // Add the root appinfo.json as well as its relative assets to the compilation. const meta = rootAppInfo(context, scan); if (meta && meta.obj) { meta.obj = compilation.applyPluginsWaterfall('webos-meta-root-appinfo', meta.obj, { path: meta.path }); handleSysAssetPath(context, meta.obj); addMetaAssets(meta.path, '', meta.obj, compilation); emitAsset('appinfo.json', compilation.assets, new Buffer(JSON.stringify(meta.obj, null, '\t'))); } // Scan for all localized appinfo.json files in the "resources" directory. let loc = glob.sync('resources/**/appinfo.json', { cwd: context, nodir: true }); loc = compilation.applyPluginsWaterfall('webos-meta-list-localized', loc); // Add each locale-specific appinfo.json and its relative assets to the compilation. let locFile, locRel, locMeta, locCode; for (let i = 0; i < loc.length; i++) { if (typeof loc[i] === 'string') { locFile = path.join(context, loc[i]); locRel = loc[i]; locMeta = readAppInfo(locFile); } else { locFile = path.join(context, loc[i].generate); locRel = loc[i].generate; locMeta = loc[i].value || {}; } if (locMeta) { locCode = path.relative(path.join(context, 'resources'), path.dirname(locFile)).replace(/[\\/]+/g, '-'); locMeta = compilation.applyPluginsWaterfall('webos-meta-localized-appinfo', locMeta, { path: locFile, locale: locCode }); handleSysAssetPath(context, locMeta); addMetaAssets(path.dirname(locFile), path.dirname(locRel), locMeta, compilation); emitAsset(locRel, compilation.assets, new Buffer(JSON.stringify(locMeta, null, '\t'))); } } callback(); }); compiler.plugin('compilation', compilation => { compilation.plugin('html-webpack-plugin-before-html-generation', (params, callback) => { const appinfo = rootAppInfo(context, scan); if (appinfo) { // When no explicit HTML document title is provided, automically use the root appinfo's title value. if ( appinfo.obj.title && (!params.plugin.options.title || params.plugin.options.title === 'Webpack App') ) { params.plugin.options.title = appinfo.obj.title; } } callback(); }); }); };