rollup-plugin-kdu
Version:
Roll .kdu files
350 lines (340 loc) • 15.8 kB
JavaScript
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var rollupPluginutils = require('rollup-pluginutils');
var queryString = require('querystring');
var path = require('path');
var componentCompiler = require('@kdujs/component-compiler');
var MagicString = _interopDefault(require('magic-string'));
var componentCompilerUtils = require('@kdujs/component-compiler-utils');
var debug = _interopDefault(require('debug'));
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
function __awaiter(thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
const GET_QUERY = /\.kdu(\.[a-z]+?)?\?(.+)$/i;
const PARAM_NAME = 'rollup-plugin-kdu';
function createKduFilter(include = [/\.kdu$/i], exclude = []) {
const filter = rollupPluginutils.createFilter(include, exclude);
return id => filter(id);
}
function getKduMetaFromQuery(id) {
const match = GET_QUERY.exec(id);
if (match) {
const query = queryString.parse(match[2]);
if (PARAM_NAME in query) {
const data = (Array.isArray(query[PARAM_NAME])
? query[PARAM_NAME][0]
: query[PARAM_NAME]);
const [type, index, lang] = data.split('.');
return (lang
? { type, lang, index: parseInt(index) } // styles.0.css
: { type, lang: index }); // script.js
}
}
return null;
}
function isKduPartRequest(id) {
return getKduMetaFromQuery(id) !== null;
}
const createKduPartRequest = ((filename, lang, type, index) => {
lang = lang || createKduPartRequest.defaultLang[type];
const match = GET_QUERY.exec(filename);
const query = match ? queryString.parse(match[2]) : {};
query[PARAM_NAME] = [type, index, lang]
.filter(it => it !== undefined)
.join('.');
return `${path.basename(filename)}?${queryString.stringify(query)}`;
});
createKduPartRequest.defaultLang = {
template: 'html',
styles: 'css',
script: 'js'
};
function parseKduPartRequest(id) {
if (!id.includes('.kdu'))
return;
const filename = id.substr(0, id.lastIndexOf('.kdu') + 4);
const params = getKduMetaFromQuery(id);
if (params === null)
return;
return {
filename,
meta: params
};
}
function resolveKduPart(descriptors, { filename, meta }) {
const descriptor = descriptors.get(filename);
if (!descriptor)
throw Error('File not processed yet, ' + filename);
const blocks = descriptor[meta.type];
const block = Array.isArray(blocks) ? blocks[meta.index] : blocks;
if (!block)
throw Error(`Requested (type=${meta.type} & index=${meta.index}) block not found in ${filename}`);
return block;
}
function transformRequireToImport(code) {
const imports = {};
let strImports = '';
code = code.replace(/require\(("(?:[^"\\]|\\.)+"|'(?:[^'\\]|\\.)+')\)/g, (_, name) => {
if (!(name in imports)) {
imports[name] = `__$_require_${name
.replace(/[^a-z0-9]/g, '_')
.replace(/_{2,}/g, '_')
.replace(/^_|_$/g, '')}__`;
strImports += 'import ' + imports[name] + ' from ' + name + '\n';
}
return imports[name];
});
return strImports + code;
}
const templateCompiler = require('kdu-template-compiler');
const hash = require('hash-sum');
const { version } = require('../package.json');
const d = debug('rollup-plugin-kdu');
const dR = debug('rollup-plugin-kdu:resolve');
const dL = debug('rollup-plugin-kdu:load');
const dT = debug('rollup-plugin-kdu:transform');
/**
* Rollup plugin for handling .kdu files.
*/
function kdu(opts = {}) {
const isKdu = createKduFilter(opts.include, opts.exclude);
const isProduction = opts.template && typeof opts.template.isProduction === 'boolean'
? opts.template.isProduction
: process.env.NODE_ENV === 'production' ||
process.env.BUILD === 'production';
d('Version ' + version);
d(`Build environment: ${isProduction ? 'production' : 'development'}`);
d(`Build target: ${process.env.KDU_ENV || 'browser'}`);
if (!opts.normalizer)
opts.normalizer = '~' + 'kdu-runtime-helpers/dist/normalize-component.mjs';
if (!opts.styleInjector)
opts.styleInjector =
'~' + 'kdu-runtime-helpers/dist/inject-style/browser.mjs';
if (!opts.styleInjectorSSR)
opts.styleInjectorSSR =
'~' + 'kdu-runtime-helpers/dist/inject-style/server.mjs';
if (!opts.styleInjectorShadow)
opts.styleInjectorShadow =
'~' + 'kdu-runtime-helpers/dist/inject-style/shadow.mjs';
createKduPartRequest.defaultLang = Object.assign(Object.assign({}, createKduPartRequest.defaultLang), opts.defaultLang);
const shouldExtractCss = opts.css === false;
const customBlocks = [];
if (opts.blackListCustomBlocks) {
console.warn('`blackListCustomBlocks` option is deprecated use `customBlocks`.');
customBlocks.push(...opts.blackListCustomBlocks.map(tag => '!' + tag));
}
if (opts.whiteListCustomBlocks) {
console.warn('`whiteListCustomBlocks` option is deprecated use `customBlocks`.');
customBlocks.push(...opts.whiteListCustomBlocks);
}
const isAllowed = createCustomBlockFilter(opts.customBlocks || customBlocks);
const beforeAssemble = opts.beforeAssemble ||
((d) => d);
const exposeFilename = typeof opts.exposeFilename === 'boolean' ? opts.exposeFilename : false;
const data = (opts.data || {});
delete opts.data;
delete opts.beforeAssemble;
delete opts.css;
delete opts.exposeFilename;
delete opts.customBlocks;
delete opts.blackListCustomBlocks;
delete opts.whiteListCustomBlocks;
delete opts.defaultLang;
delete opts.include;
delete opts.exclude;
opts.template = Object.assign({ transformAssetUrls: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
} }, opts.template);
if (opts.template && typeof opts.template.isProduction === 'undefined') {
opts.template.isProduction = isProduction;
}
const compiler = componentCompiler.createDefaultCompiler(opts);
const descriptors = new Map();
if (opts.css === false)
d('Running in CSS extract mode');
function prependStyle(id, lang, code, map) {
if (!(lang in data))
return { code };
const ms = new MagicString(code, {
filename: id,
indentExclusionRanges: []
});
const value = data[lang];
const fn = typeof value === 'function' ? value : () => value;
ms.prepend(fn());
return { code: ms.toString() };
}
return {
name: 'KduPlugin',
resolveId(id, importer) {
const request = id;
if (id.startsWith('kdu-runtime-helpers/')) {
id = require.resolve(id);
dR(`form: ${request} \nto: ${id}\n`);
return id;
}
if (!isKduPartRequest(id))
return;
// @ts-ignore
id = path.resolve(path.dirname(importer), id);
const ref = parseKduPartRequest(id);
if (ref) {
const element = resolveKduPart(descriptors, ref);
const src = element.src;
if (ref.meta.type !== 'styles' && typeof src === 'string') {
if (src.startsWith('.')) {
return path.resolve(path.dirname(ref.filename), src);
}
else {
return require.resolve(src, {
paths: [path.dirname(ref.filename)]
});
}
}
dR(`from: ${request} \nto: ${id}\n`);
return id;
}
},
load(id) {
const request = parseKduPartRequest(id);
if (!request)
return null;
const element = resolveKduPart(descriptors, request);
let code = 'code' in element
? element.code // .code is set when extract styles is used. { css: false }
: element.content;
let map = element.map;
if (request.meta.type === 'styles') {
code = prependStyle(id, request.meta.lang, code).code;
}
dL(`id: ${id}\ncode: \n${code}\nmap: ${JSON.stringify(map, null, 2)}\n\n`);
return { code, map };
},
transform(source, filename) {
return __awaiter(this, void 0, void 0, function* () {
if (isKdu(filename)) {
// Create deep copy to prevent issue during watching changes.
const descriptor = JSON.parse(JSON.stringify(componentCompilerUtils.parse({
filename,
source,
compiler: opts.compiler || templateCompiler,
compilerParseOptions: opts.compilerParseOptions,
sourceRoot: opts.sourceRoot,
needMap: 'needMap' in opts ? opts.needMap : true
})));
descriptors.set(filename, descriptor);
const scopeId = 'data-k-' +
(isProduction
? hash(path.basename(filename) + source)
: hash(filename + source));
const styles = yield Promise.all(descriptor.styles.map((style) => __awaiter(this, void 0, void 0, function* () {
if (style.content) {
style.content = prependStyle(filename, style.lang || 'css', style.content, style.map).code;
}
const compiled = yield compiler.compileStyleAsync(filename, scopeId, style);
if (compiled.errors.length > 0)
throw Error(compiled.errors[0]);
return compiled;
})));
const input = {
scopeId,
styles,
customBlocks: []
};
if (descriptor.template) {
input.template = compiler.compileTemplate(filename, descriptor.template);
input.template.code = transformRequireToImport(input.template.code);
if (input.template.errors && input.template.errors.length) {
input.template.errors.map((error) => this.error(error));
}
if (input.template.tips && input.template.tips.length) {
input.template.tips.map((message) => this.warn({ message }));
}
}
input.script = descriptor.script
? {
code: `
export * from '${createKduPartRequest(filename, descriptor.script.lang || 'js', 'script')}'
import script from '${createKduPartRequest(filename, descriptor.script.lang || 'js', 'script')}'
export default script
${exposeFilename
? `
// For security concerns, we use only base name in production mode.
script.__file = ${isProduction
? JSON.stringify(path.basename(filename))
: JSON.stringify(filename)}`
: ''}
`
}
: { code: '' };
if (shouldExtractCss) {
input.styles = input.styles
.map((style, index) => {
descriptor.styles[index].code = style.code;
input.script.code +=
'\n' +
`import '${createKduPartRequest(filename, 'css', 'styles', index)}'`;
if (style.module || descriptor.styles[index].scoped) {
return Object.assign(Object.assign({}, style), { code: '', map: undefined });
}
})
.filter(Boolean);
}
input.script.code = input.script.code.replace(/^\s+/gm, '');
const result = componentCompiler.assemble(compiler, filename, beforeAssemble(input), opts);
descriptor.customBlocks.forEach((block, index) => {
if (!isAllowed(block.type))
return;
result.code +=
'\n' +
`export * from '${createKduPartRequest(filename, (typeof block.attrs.lang === 'string' && block.attrs.lang) ||
createKduPartRequest.defaultLang[block.type] ||
block.type, 'customBlocks', index)}'`;
});
dT(`id: ${filename}\ncode:\n${result.code}\n\nmap:\n${JSON.stringify(result.map, null, 2)}\n`);
result.map = result.map || { mappings: '' };
return result;
}
});
}
};
}
function createCustomBlockFilter(customBlocks) {
if (typeof customBlocks === 'function')
return customBlocks;
if (!Array.isArray(customBlocks))
return () => false;
const allowed = new Set(customBlocks.filter(tag => !tag.startsWith('!')));
const notAllowed = new Set(customBlocks.filter(tag => tag.startsWith('!')).map(tag => tag.substr(1)));
return tag => {
if (allowed.has(tag))
return true;
if (notAllowed.has(tag))
return false;
if (notAllowed.has('*'))
return false;
return allowed.has('*');
};
}
module.exports = kdu;
//# sourceMappingURL=rollup-plugin-kdu.js.map
;