ng2-fittext/tasks/inline-resources.js
2017-11-14 11:07:08 +01:00

186 lines
5.1 KiB
JavaScript

'use strict';
// original code by the Angular Material 2 team
const fs = require('fs');
const glob = require('glob');
const path = require('path');
const sass = require('node-sass');
const inlineResources = (globs, sourcePrefix) => {
if (typeof globs === 'string') {
globs = [globs];
}
return Promise.all(
globs.map((pattern) => replaceSource(pattern, sourcePrefix))
);
};
const replaceSource = (pattern, sourcePrefix) => {
// pattern is a directory
if (pattern.indexOf('*') === -1) {
pattern = path.join(pattern, '**', '*');
}
return new Promise((resolve, reject) => {
glob(pattern, {}, (error, files) => {
if (error) reject(Error);
files.filter((name) => /\.ts$/.test(name)).forEach((filePath) => {
try {
inlineFileResources(filePath, sourcePrefix);
} catch (readError) {
reject(readError);
}
});
resolve();
});
});
};
const inlineFileResources = (filePath, sourcePrefix) => {
const content = fs.readFileSync(filePath, 'utf8');
const inlineContents = inlineResourcesFromString(content, sourcePrefix, (url) =>
path.join(path.dirname(filePath), url)
);
fs.writeFileSync(filePath, inlineContents);
};
const inlineResourcesFromString = (content, sourcePrefix, callback) => [
inlineTemplate, inlineStyle, removeModuleId
].reduce((final, method) => method(final, sourcePrefix, callback), content);
const inlineTemplate = (content, sourcePrefix, callback) =>
content.replace(/templateUrl:\s*'([^']+?\.html)'/g, (match, url) => {
const mini = getMiniContents(url, sourcePrefix, callback);
return `template: "${mini}"`;
});
const inlineStyle = (content, sourcePrefix, callback) =>
content.replace(/styleUrls:\s*(\[[\s\S]*?\])/gm, (match, styleUrls) => {
const urls = eval(styleUrls); // string -> array
return 'styles: [' + urls.map((url) => {
const mini = getMiniContents(url, sourcePrefix, callback);
return `"${mini}"`;
}).join(',\n') + ']';
});
const getMiniContents = (url, sourcePrefix, callback) => {
const srcFile = callback(url);
const file = srcFile.replace(/^dist/, sourcePrefix)
const srcDir = file.slice(0, file.lastIndexOf(path.sep));
let template = '';
if (file.match(/\.s(a|c)ss$/)) {
// convert SASS -> CSS
template = sass.renderSync({
file,
importer: (url) => handleSassImport(url, srcDir)
});
template = template.css.toString();
} else {
template = fs.readFileSync(file, 'utf8');
}
return minifyText(template);
};
const handleSassImport = (url, srcDir) => {
const fullUrl = getFullSassUrl(url, srcDir);
let isPartial = false;
let validUrls = getSassUrls(fullUrl);
// if we can't find the file, try to
// see find it as a partial (underscore-prefixed)
if (validUrls.length === 0) {
validUrls = getSassUrls(fullUrl, true);
isPartial = true;
}
const file = getSassImportUrl(validUrls);
// CSS files don't get compiled in
return /\.css$/.test(file) ?
{ contents: fs.readFileSync(file, 'utf8') } :
{ file };
};
const getSassUrls = (url, partial) => {
let extensions = ['sass', 'scss'];
if (!partial) {
extensions = extensions.concat('', 'css');
} else {
const lastSlash = url.lastIndexOf(path.sep);
const urlDir = url.slice(0, lastSlash);
const fileName = url.slice(lastSlash + 1);
if (fileName[0] !== '_') {
url = urlDir + path.sep + '_' + fileName;
}
}
return extensions.reduce((valid, extension) => {
const extensionUrl = verifyUrl(url, extension);
if (extensionUrl) {
valid = valid.concat(extensionUrl);
}
return valid;
}, []);
};
const verifyUrl = (url, extension) => {
if (extension) {
url = url + `.${ extension }`;
}
if (!fs.existsSync(url)) {
url = null;
}
return url;
}
// convert ~-prefixed filenames to node_modules-prefixed
// make all others relative to srcDir
const getFullSassUrl = (url, srcDir) =>
/^~/.test(url) ?
path.resolve('node_modules', url.slice(1)) :
path.resolve(srcDir, url);
const getSassImportUrl = (validUrls) => {
if (validUrls.length !== 1) {
let error = 'Cannot determine Sass/CSS file to process. ';
if (validUrls.length === 0) {
error = error + `\n There are no files matching ${ url }`;
} else {
error = error + 'Candidates:\n ' + validUrls.join('\n ')
+ '\nPlease delete or rename all but one of these files or specify the extension to use.';
}
throw new Error(error);
}
return validUrls[0];
};
const minifyText = (text) => text
.replace(/([\n\r]\s*)+/gm, ' ')
.replace(/"/g, '\\"');
const removeModuleId = (content) =>
content.replace(/\s*moduleId:\s*module\.id\s*,?\s*/gm, '');
module.exports = inlineResources;
if (!module.parent) {
inlineResources('./build', 'src');
}