diff --git a/common/traits/controllable.js b/common/traits/controllable.trait.js similarity index 100% rename from common/traits/controllable.js rename to common/traits/controllable.trait.js diff --git a/common/traits/informed.js b/common/traits/informed.trait.js similarity index 100% rename from common/traits/informed.js rename to common/traits/informed.trait.js diff --git a/generate-trait-defs.js b/generate-trait-defs.js index ec39146..75807d3 100644 --- a/generate-trait-defs.js +++ b/generate-trait-defs.js @@ -1,53 +1,58 @@ +const glob = require('glob'); +const {getOptions} = require('loader-utils'); const path = require('path'); -const glob = require('glob'); - // Dynamically require all traits. -module.exports = (source) => { - const nodeModulesPath = path.resolve( - __dirname, 'node_modules', - ); - const traitPaths = [ - { - pathGlob: path.resolve( - nodeModulesPath, - '@avocado', - 'entity', - 'traits', - '*.js' - ), - replacer: (dirname) => { - return dirname.replace(`${nodeModulesPath}/`, ''); - }, - }, - { - pathGlob: path.resolve(__dirname, 'common', 'traits', '*.js'), - replacer: (dirname) => { - return dirname.replace(__dirname, '.'); - }, - }, - ]; - let lines = []; - traitPaths.forEach(({pathGlob, replacer}) => { - const files = glob.sync(pathGlob); - const modules = files.map((file) => { - let dirname = path.dirname(file); - dirname = replacer(dirname); - const basename = path.basename(file, '.js'); - return `${dirname}/${basename}`; +module.exports = function(source) { + const options = getOptions(this); + if (!options.paths) { + options.paths = []; + } + // Search avocado. + options.paths.push(path.resolve( + __dirname, 'node_modules', '@avocado', + )); + // Get all trait source paths. + const sourcePaths = []; + options.paths.forEach((path) => { + const files = glob.sync(`${path}/**/*.trait.js`, { + follow: true, }); - const moduleLines = modules.map((module_) => { - const basename = path.basename(module_); - const parts = basename.split('-'); - const className = parts.reduce((className, part) => { - const firstLetter = part.charAt(0).toUpperCase(); - const rest = part.substr(1).toLowerCase(); - return className + firstLetter + rest; - }, ''); - return `import {${className}} from '${module_}';\nregisterTrait(${className});\n`; - }); - lines = lines.concat(moduleLines); + sourcePaths.push(...files); }); - lines.unshift(`import {registerTrait} from '@avocado/entity/trait-registry';\n`); - return lines.join('\n'); + // Build import definitions. + const importDefinitions = sourcePaths.map((sourcePath) => { + const relativePath = path.relative(__dirname, sourcePath); + const basename = path.basename(relativePath, '.trait.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 = `./${parts.join('/')}` + } + const importPath = `${importDirectory}/${basename}.trait`; + // 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; + }, ''); + // Import and register. + return [ + `import {${className}} from '${importPath}';`, + `registerTrait(${className});`, + ].join('\n'); + }); + // Import trait registry first. + return [ + `import {registerTrait} from '@avocado/entity/trait-registry'`, + '', + ...importDefinitions + ].join('\n'); } diff --git a/webpack.common.config.js b/webpack.common.config.js index 0b8d8b3..49cf452 100644 --- a/webpack.common.config.js +++ b/webpack.common.config.js @@ -18,9 +18,16 @@ const config = { }, { test: /register-traits.js/, - use: { - loader: './generate-trait-defs', - }, + use: [ + { + loader: './generate-trait-defs', + options: { + paths: [ + path.resolve(__dirname, 'common'), + ], + } + }, + ], }, ], },