source/Component.js
- import 'dom-renderer/dist/polyfill';
-
- import { stringifyDOM } from 'dom-renderer';
-
- import {readFile, readdir, statSync, outputFile, remove} from 'fs-extra';
-
- import {join, basename, dirname, extname} from 'path';
-
- import Package from 'amd-bundle';
-
- import * as Parser from './parser';
-
- import { folderOf, metaOf } from './utility';
-
- import { toDataURI } from '@tech_query/node-toolkit';
-
-
- /**
- * Component packer
- */
- export default class Component {
- /**
- * @param {string} path - Component directory
- */
- constructor(path) {
-
- this.path = path;
-
- this.name = basename( path );
-
- this.entry = join(path, 'index');
- }
-
- /**
- * @param {string} path
- *
- * @return {DocumentFragment}
- */
- static async parseHTML(path) {
-
- const box = document.createElement('div'),
- fragment = document.createDocumentFragment();
-
- box.innerHTML = (await readFile( path )) + '';
-
- fragment.append(... box.childNodes);
-
- return fragment;
- }
-
- /**
- * @param {string} source - File path or Style source code
- * @param {?string} type - MIME type
- * @param {string} [base] - Path of the file which `@import` located in
- *
- * @return {?Element} Style element
- */
- static async parseCSS(source, type, base) {
-
- var style;
-
- type = type ? type.split('/')[1] : extname( source ).slice(1);
-
- if (! source.includes('\n'))
- source = await readFile(base = source) + '';
-
- style = Parser[type] && await Parser[type](source, dirname( base ));
-
- return style && Object.assign(
- document.createElement('style'), {textContent: style}
- );
- }
-
- /**
- * @param {DocumentFragment} fragment
- *
- * @return {Element[]}
- */
- static findStyle(fragment) {
-
- return [ ].concat.apply([ ], Array.from(
- fragment.querySelectorAll('link[rel="stylesheet"], template'),
- tag => tag.content ?
- Array.from( tag.content.querySelectorAll('style') ) : tag
- ));
- }
-
- /**
- * @param {string} tagName
- *
- * @return {string}
- */
- static identifierOf(tagName) {
-
- return tagName[0].toUpperCase() +
- tagName.replace(/-(\w)/g, (_, char) => char.toUpperCase()).slice(1);
- }
-
- /**
- * @param {String} path - Full name of a JS file
- *
- * @return {String} Packed JS source code
- */
- static packJS(path) {
-
- const single_entry = join(folderOf().lib || '', 'index.js');
-
- const name = (path === single_entry) && metaOf().name;
-
- path = path.split('.').slice(0, -1).join('.');
-
- return (new Package( path )).bundle(
- name || basename( dirname( path ) )
- );
- }
-
- /**
- * @return {DocumentFragment} HTML version bundle of this component
- */
- async toHTML() {
-
- const fragment = await Component.parseHTML(this.entry + '.html'),
- CSS = [ ];
-
- for (let sheet of Component.findStyle( fragment )) {
-
- let style = await Component.parseCSS(
- (sheet.tagName === 'STYLE') ?
- sheet.textContent : join(this.path, sheet.getAttribute('href')),
- sheet.type,
- this.entry + '.css'
- );
-
- if (! style) continue;
-
- sheet.replaceWith( style );
-
- if (style.parentNode === fragment) CSS.push( style );
- }
-
- fragment.querySelector('template').content.prepend(... CSS);
-
- const script = fragment.querySelector('script');
-
- if ( script )
- script.replaceWith(Object.assign(document.createElement('script'), {
- text: `\n${
- Component.packJS( join(this.path, script.getAttribute('src')) )
- }\n`
- }));
-
- return fragment;
- }
-
- /**
- * @protected
- *
- * @param {String} file - File path
- *
- * @return {String} Legal ECMAScript source code
- */
- async assetOf(file) {
-
- switch ( extname( file ).slice(1) ) {
- case 'html':
- case 'htm':
- file = stringifyDOM(await this.toHTML()); break;
- case 'css':
- case 'less':
- case 'sass':
- case 'scss':
- case 'stylus':
- file = (await Component.parseCSS( file )).textContent; break;
- case 'js':
- return;
- case 'json':
- return (await readFile( file )) + '';
- case 'yaml':
- case 'yml':
- return Parser.yaml((await readFile( file )) + '');
- default:
- file = toDataURI( file );
- }
-
- return JSON.stringify( file );
- }
-
- /**
- * @return {string} JS version bundle of this component
- */
- async toJS() {
-
- const temp_file = [ ];
-
- for (let file of await readdir( this.path )) {
-
- file = join(this.path, file);
-
- if (! statSync( file ).isFile()) continue;
-
- let temp = `${file}.js`;
-
- file = await this.assetOf( file );
-
- if (!(file != null)) continue;
-
- temp_file.push( temp );
-
- await outputFile(temp, `export default ${file}`);
- }
-
- const source = Component.packJS(this.entry + '.js');
-
- await Promise.all( temp_file.map(file => remove( file )) );
-
- return source;
- }
- }