feat: dynamic webpack :o

This commit is contained in:
cha0s 2019-11-22 02:13:32 -06:00
parent e49ef9b54c
commit 631106b29c
10 changed files with 276 additions and 23 deletions

View File

@ -0,0 +1,15 @@
const path = require('path');
module.exports = ({entries, paths, root, rules}) => {
rules.push({
test: new RegExp('@avocado/packages/core/hook/entry.js$'),
use: {
loader: '@avocado/core/hook/loader',
options: {
paths,
root,
},
},
});
entries.push('@avocado/core/hook/entry');
};

View File

@ -1,7 +1,11 @@
const path = require('path');
const glob = require('glob');
const {getOptions} = require('loader-utils');
const path = require('path');
const validateOptions = require('schema-utils');
const {lookupSourcePaths} = require('../webpack/util');
const schema = {
type: 'object',
properties: {
@ -21,36 +25,19 @@ const schema = {
module.exports = function(source) {
const options = getOptions(this);
// Validate schema.
validateOptions(schema, options, 'Avocado hooks');
validateOptions(schema, options, '[avocado] Hooks');
// Extract options.
const {paths} = options;
// Search avocado.
const avocadoPath = path.normalize(
path.join(__dirname, '..', '..'),
);
paths.push(avocadoPath);
// Get source paths.
const sourcePaths = [];
paths.forEach((path) => {
const files = glob.sync(`${path}/**/hooks.js`, {
follow: true,
});
sourcePaths.push(...files);
});
const {paths, root} = options;
// Build registers.
const sourcePaths = lookupSourcePaths(paths, 'hooks.js');
const registers = sourcePaths.map((sourcePath) => {
const relativePath = path.relative(__dirname, sourcePath);
const basename = path.basename(relativePath, '.js');
const relativePath = path.relative(root, sourcePath);
const parts = relativePath.split('/');
// Chop off basename.
parts.pop();
// Module or local?
let importDirectory;
if ('..' === parts[0]) {
const avocadoPathParts = ['@avocado', 'core', 'hook'].concat(parts);
importDirectory = path.normalize(avocadoPathParts.join('/'));
}
else if ('node_modules' === parts[0]) {
if ('node_modules' === parts[0]) {
importDirectory = parts.slice(1).join('/');
}
else {

View File

@ -0,0 +1,92 @@
const path = require('path');
const glob = require('glob');
const {getOptions} = require('loader-utils');
const validateOptions = require('schema-utils');
const {lookupSourcePaths} = require('./util');
const schema = {
type: 'object',
properties: {
paths: {
items: {
type: 'string',
},
type: 'array',
},
registrar: {
type: 'object',
properties: {
function: {
type: 'string',
},
module: {
type: 'string',
},
},
required: [
'function',
'module',
],
},
type: {
type: 'string',
},
},
required: [
'registrar',
'type',
],
};
// Dynamically require all traits.
module.exports = function(source) {
const options = getOptions(this);
// Validate schema.
validateOptions(schema, options, '[avocado] Autotomatic registration');
// Extract options.
const {classTransformer: transformer, paths, registrar, root, type} = options;
// Search avocado.
paths.push(path.resolve(
__dirname, 'node_modules', '@avocado',
));
// Get all trait source paths.
const sourcePaths = lookupSourcePaths(paths, `*.${type}.js`);
// Build import definitions.
const importDefinitions = sourcePaths.map((sourcePath) => {
const relativePath = path.relative(root, sourcePath);
const basename = path.basename(relativePath, `.${type}.js`);
const parts = relativePath.split('/');
// Chop off basename.
parts.pop();
// Module or local?
let importDirectory;
if ('node_modules' === parts[0]) {
importDirectory = `${parts.slice(1).join('/')}`;
}
else {
importDirectory = `${root}/${parts.join('/')}`
}
const importPath = `${importDirectory}/${basename}.${type}`;
// Extract class name.
const baseparts = basename.split('-');
const className = baseparts.reduce((className, part) => {
const firstLetter = part.charAt(0).toUpperCase();
const rest = part.substr(1).toLowerCase();
return className + firstLetter + rest;
}, '');
const transformed = transformer ? transformer(className) : className;
// Import and register.
return [
`${registrar.function}(require('${importPath}').${transformed});`,
].join('\n');
});
// Import trait registry first.
const output = [
`import {${registrar.function}} from '${registrar.module}'`,
'',
...importDefinitions
].join('\n');
return output;
}

View File

@ -0,0 +1,32 @@
const path = require('path');
const glob = require('glob');
const {getOptions} = require('loader-utils');
const validateOptions = require('schema-utils');
const {lookupSourcePaths} = require('../webpack/util');
const schema = {
type: 'object',
properties: {
entries: {
items: {
type: 'string',
},
type: 'array',
},
},
required: [
'entries',
],
};
// Dynamically require all traits.
module.exports = function(source) {
const options = getOptions(this);
validateOptions(schema, options, 'Avocado core');
const {entries} = options;
return entries.reduce((r, entry) => {
return r + `import '${entry}';`;
}, '');
}

View File

@ -0,0 +1,46 @@
const path = require('path');
const {lookupSourcePaths} = require('./util');
exports.AvocadoPlugin = class AvocadoPlugin {
constructor(root, side) {
this.root = root;
this.side = side;
}
apply(compiler) {
const {options} = compiler;
const {module} = options;
const entries = [
'@babel/polyfill',
];
const paths = [
path.resolve(this.root, 'common'),
path.resolve(this.root, this.side),
];
const sourcePaths = lookupSourcePaths(paths, '*.avopack.js');
sourcePaths.forEach((sourcePath) => {
require(`${this.root}/${sourcePath}`)({
compiler,
entries,
options,
paths,
root: this.root,
rules: options.module.rules,
side: this.side,
});
});
module.rules.push({
test: new RegExp('@avocado/packages/core/webpack/entry.js$'),
use: {
loader: '@avocado/core/webpack/loader',
options: {
entries,
},
},
});
}
}

View File

@ -0,0 +1,18 @@
const path = require('path');
const glob = require('glob');
exports.lookupSourcePaths = function(paths, pattern) {
// Include avocado.
const avocadoPath = './node_modules/@avocado';
paths.push(avocadoPath);
// Get source paths.
const sourcePaths = [];
paths.forEach((path) => {
const files = glob.sync(`${path}/**/${pattern}`, {
follow: true,
});
sourcePaths.push(...files);
});
return sourcePaths;
}

View File

@ -0,0 +1,20 @@
const path = require('path');
module.exports = ({entries, paths, root, rules}) => {
rules.push({
test: new RegExp('@avocado/packages/entity/trait/traits.avopack.js$'),
use: {
loader: '@avocado/core/webpack/autoreg',
options: {
paths,
registrar: {
function: 'registerTrait',
module: '@avocado/entity',
},
root,
type: 'trait',
},
},
});
entries.push('@avocado/entity/trait/traits.avopack');
};

View File

@ -0,0 +1,23 @@
const path = require('path');
module.exports = ({entries, paths, root, rules}) => {
rules.push({
test: new RegExp('@avocado/packages/net/packet/packets.avopack.js$'),
use: {
loader: '@avocado/core/webpack/autoreg',
options: {
classTransformer: (Packet) => {
return `${Packet}Packet`;
},
paths,
registrar: {
function: 'registerPacket',
module: '@avocado/net',
},
root,
type: 'packet',
},
},
});
entries.push('@avocado/net/packet/packets.avopack');
};

View File

@ -0,0 +1,20 @@
const path = require('path');
module.exports = ({entries, paths, root, rules}) => {
rules.push({
test: new RegExp('@avocado/packages/net/s13n/s13n.avopack.js$'),
use: {
loader: '@avocado/core/webpack/autoreg',
options: {
paths,
registrar: {
function: 'registerSynchronized',
module: '@avocado/net',
},
root,
type: 'synchronized',
},
},
});
entries.push('@avocado/net/s13n/s13n.avopack');
};