diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..9b98474 --- /dev/null +++ b/.babelrc @@ -0,0 +1,10 @@ +{ + "presets": [[ + "env", + { + "targets": { + "node": "4" + } + } + ]] +} \ No newline at end of file diff --git a/lib/loader.js b/lib/loader.js index 5645fac..eacc021 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -3,110 +3,103 @@ var fs = require ("fs"); var loaderUtils = require ("loader-utils"); var path = require ("path"); var tmp = require ("tmp"); -var validateOptions = require ("schema-utils"); +var schemaUtils = require ("schema-utils"); var schema = require ("./options.json"); +/* eslint-disable + multiline-ternary, +*/ +module.exports = function(content) { + if (!this.emitFile) throw new Error('SWF Loader\n\nemitFile is required from module system'); + + var options = loaderUtils.getOptions(this) || {}; + + schemaUtils(schema, options, 'SWF Loader'); + + var context = options.context || this.rootContext || this.options && this.options.context; + + var url = loaderUtils.interpolateName(this, options.name, { + context, + content, + regExp: options.regExp + }); -module.exports = function (content, map) { - - if (!this.emitFile) { - - throw new Error ("SWF Loader\n\nemitFile is required from module system"); - - } - - this.cacheable && this.cacheable (); - this.value = content; - - var options = loaderUtils.getOptions (this) || {}; - - validateOptions (schema, options, "SWF Loader"); - - var context = options.context || this.rootContext || this.options && this.options.context - var url = loaderUtils.interpolateName (this, options.name, { context, content, regExp: options.regExp }); - var outputPath = ""; - - if (options.outputPath) { - - outputPath = (typeof options.outputPath === "function" ? options.outputPath (url) : options.outputPath); - - } - - var filePath = this.resourcePath; - - if (options.useRelativePath) { - - var issuerContext = (this._module && this._module.issuer && this._module.issuer.context) || context; - - var relativeUrl = issuerContext && path.relative (issuerContext, filePath).split (path.sep).join ('/'); - var relativePath = relativeUrl && `${path.dirname(relativeUrl)}/`; - - // eslint-disable-next-line no-bitwise - if (~relativePath.indexOf ("../")) { - - outputPath = path.posix.join (outputPath, relativePath, url); - - } else { - - outputPath = relativePath + url; - - } - - url = relativePath + url; - - } else if (options.outputPath) { - - outputPath = typeof options.outputPath === "function" ? options.outputPath (url) : options.outputPath + url; - url = outputPath; - - } else { - - outputPath = url; - - } - - outputPath = path.basename (outputPath, ".swf") + ".bundle"; - url = path.basename (url, ".swf") + ".bundle"; - - var publicPath = `__webpack_public_path__ + ${JSON.stringify (url)}`; - - if (options.publicPath !== undefined) { - - publicPath = JSON.stringify (typeof options.publicPath === "function" ? options.publicPath (url) : options.publicPath + url); - - } - - if (options.emitFile === undefined || options.emitFile) { - - tmp.setGracefulCleanup (); - var tempDirectory = tmp.dirSync ({ postfix: ".bundle" }); - - execSync ("openfljs process " + this.resourcePath + " " + tempDirectory.name); - - var walkSync = function (baseDir, dir = "", filelist = []) { - - fs.readdirSync (baseDir).forEach (function (file) { - - filelist = fs.statSync (path.join (baseDir, file)).isDirectory () - ? walkSync (path.join (baseDir, file), path.join (dir, file), filelist) - : filelist.concat (path.join (dir, file)); - - }); - - return filelist; - - } - - walkSync (tempDirectory.name).forEach (function (file) { - - this.emitFile (path.join (outputPath, file), fs.readFileSync (path.join (tempDirectory.name, file))); - - }.bind (this)); - - } - - return `module.exports = ${publicPath};`; - + var outputPath = url; + + if (options.outputPath) { + if (typeof options.outputPath === 'function') { + outputPath = options.outputPath(url); + } else { + outputPath = path.posix.join(options.outputPath, url); + } + } + + if (options.useRelativePath) { + var filePath = this.resourcePath; + + var issuer = options.context ? context : this._module && this._module.issuer && this._module.issuer.context; + + var relativeUrl = issuer && path.relative(issuer, filePath).split(path.sep).join('/'); + + var relativePath = relativeUrl && `${path.dirname(relativeUrl)}/`; + // eslint-disable-next-line no-bitwise + if (~relativePath.indexOf('../')) { + outputPath = path.posix.join(outputPath, relativePath, url); + } else { + outputPath = path.posix.join(relativePath, url); + } + } + + var outputPathBundle = outputPath.replace(/\.swf$/, '') + '.bundle'; + var publicPath = `__webpack_public_path__ + ${JSON.stringify(outputPathBundle)}`; + + if (options.publicPath) { + if (typeof options.publicPath === 'function') { + publicPath = options.publicPath(url); + } else if (options.publicPath.endsWith('/')) { + publicPath = options.publicPath + url; + } else { + publicPath = `${options.publicPath}/${url}`; + } + + publicPath = publicPath.replace(/\.swf$/, '') + '.bundle'; + publicPath = JSON.stringify(publicPath); + } + + outputPath = outputPath.replace(/\.swf$/, '') + '.bundle'; + + if (options.emitFile === undefined || options.emitFile) { + + tmp.setGracefulCleanup (); + var tempDirectory = tmp.dirSync ({ postfix: ".bundle" }); + + execSync ("openfljs process " + this.resourcePath + " " + tempDirectory.name); + + var walkSync = function (baseDir, dir = "", filelist = []) { + + fs.readdirSync (baseDir).forEach (function (file) { + + filelist = fs.statSync (path.join (baseDir, file)).isDirectory () + ? walkSync (path.join (baseDir, file), path.join (dir, file), filelist) + : filelist.concat (path.join (dir, file)); + + }); + + return filelist; + + } + + walkSync (tempDirectory.name).forEach (function (file) { + + this.emitFile (path.join (outputPath, file), fs.readFileSync (path.join (tempDirectory.name, file))); + + }.bind (this)); + + } + + // TODO revert to ES2015 Module export, when new CSS Pipeline is in place + return `module.exports = ${publicPath};`; + } module.exports.raw = true; \ No newline at end of file diff --git a/lib/options.json b/lib/options.json index 38db2ce..bab6fd5 100644 --- a/lib/options.json +++ b/lib/options.json @@ -1,16 +1,19 @@ { - "type": "object", - "properties": { - "name": {}, - "regExp": {}, - "context": { - "type": "string" - }, - "publicPath": {}, - "outputPath": {}, - "useRelativePath": { - "type": "boolean" - } - }, - "additionalProperties": true + "type": "object", + "properties": { + "name": {}, + "regExp": {}, + "context": { + "type": "string" + }, + "publicPath": {}, + "outputPath": {}, + "useRelativePath": { + "type": "boolean" + }, + "emitFile": { + "type": "boolean" + } + }, + "additionalProperties": true } \ No newline at end of file diff --git a/package.json b/package.json index ce742ce..7f5dc37 100644 --- a/package.json +++ b/package.json @@ -24,5 +24,16 @@ "peerDependencies": { "openfl": "^7.0.0-beta.7", "webpack": "^2.0.0 || ^3.0.0" + }, + "scripts": { + "test": "jest" + }, + "devDependencies": { + "babel-jest": "^22.4.3", + "babel-preset-env": "^1.6.1", + "jest": "^22.4.3", + "memory-fs": "^0.4.1", + "openfl": "^7.1.2", + "webpack": "^3.0.0" } } diff --git a/test/__snapshots__/loader.test.js.snap b/test/__snapshots__/loader.test.js.snap new file mode 100644 index 0000000..8843ef2 --- /dev/null +++ b/test/__snapshots__/loader.test.js.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Loader Defaults 1`] = `"module.exports = __webpack_public_path__ + \\"9baf1f9461ece62ac1a2ae67730b00c8.bundle\\";"`; diff --git a/test/fixtures/fixture.js b/test/fixtures/fixture.js new file mode 100644 index 0000000..b576e60 --- /dev/null +++ b/test/fixtures/fixture.js @@ -0,0 +1,4 @@ +/* eslint-disable */ +import swf from './layout.swf'; + +export default swf; \ No newline at end of file diff --git a/test/fixtures/layout.swf b/test/fixtures/layout.swf new file mode 100644 index 0000000..39f8d3f Binary files /dev/null and b/test/fixtures/layout.swf differ diff --git a/test/helpers/compiler.js b/test/helpers/compiler.js new file mode 100644 index 0000000..73e1397 --- /dev/null +++ b/test/helpers/compiler.js @@ -0,0 +1,68 @@ +/* eslint-disable + import/order, + multiline-ternary, + no-param-reassign, +*/ +//import del from 'del'; +import path from 'path'; +import webpack from 'webpack'; +import MemoryFS from 'memory-fs'; + +const module = (config) => { + return { + rules: config.rules || config.loader + ? [ + { + test: config.loader.test || /\.swf$/, + use: { + loader: path.resolve(__dirname, '../../lib/loader.js'), + options: config.loader.options || {}, + }, + }, + ] + : [], + }; +}; + +const plugins = config => ([ + new webpack.optimize.CommonsChunkPlugin({ + name: ['runtime'], + minChunks: Infinity, + }), +].concat(config.plugins || [])); + +const output = (config) => { + return { + path: path.resolve( + __dirname, + `../outputs/${config.output ? config.output : ''}`, + ), + filename: '[name].bundle.js', + }; +}; + +export default function (fixture, config, options) { + // webpack Config + config = { + devtool: config.devtool || 'sourcemap', + context: path.resolve(__dirname, '..', 'fixtures'), + entry: `./${fixture}`, + output: output(config), + module: module(config), + plugins: plugins(config), + }; + // Compiler Options + options = Object.assign({ output: false }, options); + + if (options.output) del.sync(config.output.path); + + const compiler = webpack(config); + + if (!options.output) compiler.outputFileSystem = new MemoryFS(); + + return new Promise((resolve, reject) => compiler.run((err, stats) => { + if (err) reject(err); + + resolve(stats); + })); +} \ No newline at end of file diff --git a/test/loader.test.js b/test/loader.test.js new file mode 100644 index 0000000..5daf2d9 --- /dev/null +++ b/test/loader.test.js @@ -0,0 +1,21 @@ +/* eslint-disable + prefer-destructuring, +*/ +import webpack from './helpers/compiler'; + +describe('Loader', () => { + test('Defaults', async () => { + const config = { + loader: { + test: /\.swf$/, + options: {}, + }, + }; + + const stats = await webpack('fixture.js', config); + + const { source } = stats.toJson().modules[1]; + + expect(source).toMatchSnapshot(); + }); +}); diff --git a/test/options/__snapshots__/outputPath.test.js.snap b/test/options/__snapshots__/outputPath.test.js.snap new file mode 100644 index 0000000..32d0db7 --- /dev/null +++ b/test/options/__snapshots__/outputPath.test.js.snap @@ -0,0 +1,101 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Options outputPath {Function} 1`] = ` +Object { + "assets": Array [ + "output_path_func/9baf1f9461ece62ac1a2ae67730b00c8.bundle/library.json", + "output_path_func/9baf1f9461ece62ac1a2ae67730b00c8.bundle/swflite.bin", + ], + "source": "module.exports = __webpack_public_path__ + \\"output_path_func/9baf1f9461ece62ac1a2ae67730b00c8.bundle\\";", +} +`; + +exports[`Options outputPath {Function} with \`options.name\` 1`] = ` +Object { + "assets": Array [ + "output_path_func/layout.bundle/library.json", + "output_path_func/layout.bundle/swflite.bin", + ], + "source": "module.exports = __webpack_public_path__ + \\"output_path_func/layout.bundle\\";", +} +`; + +exports[`Options outputPath {Function} with \`publicPath\` {Function} 1`] = ` +Object { + "assets": Array [ + "output_path_func/9baf1f9461ece62ac1a2ae67730b00c8.bundle/library.json", + "output_path_func/9baf1f9461ece62ac1a2ae67730b00c8.bundle/swflite.bin", + ], + "source": "module.exports = \\"public_path_func/9baf1f9461ece62ac1a2ae67730b00c8.bundle\\";", +} +`; + +exports[`Options outputPath {Function} with \`publicPath\` {String} 1`] = ` +Object { + "assets": Array [ + "output_path_func/9baf1f9461ece62ac1a2ae67730b00c8.bundle/library.json", + "output_path_func/9baf1f9461ece62ac1a2ae67730b00c8.bundle/swflite.bin", + ], + "source": "module.exports = \\"public_path/9baf1f9461ece62ac1a2ae67730b00c8.bundle\\";", +} +`; + +exports[`Options outputPath {String} 1`] = ` +Object { + "assets": Array [ + "output_path/9baf1f9461ece62ac1a2ae67730b00c8.bundle/library.json", + "output_path/9baf1f9461ece62ac1a2ae67730b00c8.bundle/swflite.bin", + ], + "source": "module.exports = __webpack_public_path__ + \\"output_path/9baf1f9461ece62ac1a2ae67730b00c8.bundle\\";", +} +`; + +exports[`Options outputPath {String} with \`options.name\` 1`] = ` +Object { + "assets": Array [ + "output_path/layout.bundle/library.json", + "output_path/layout.bundle/swflite.bin", + ], + "source": "module.exports = __webpack_public_path__ + \\"output_path/layout.bundle\\";", +} +`; + +exports[`Options outputPath {String} with \`publicPath\` {Function} 1`] = ` +Object { + "assets": Array [ + "output_path/9baf1f9461ece62ac1a2ae67730b00c8.bundle/library.json", + "output_path/9baf1f9461ece62ac1a2ae67730b00c8.bundle/swflite.bin", + ], + "source": "module.exports = \\"public_path_func/9baf1f9461ece62ac1a2ae67730b00c8.bundle\\";", +} +`; + +exports[`Options outputPath {String} with \`publicPath\` {String} 1`] = ` +Object { + "assets": Array [ + "output_path/9baf1f9461ece62ac1a2ae67730b00c8.bundle/library.json", + "output_path/9baf1f9461ece62ac1a2ae67730b00c8.bundle/swflite.bin", + ], + "source": "module.exports = \\"public_path/9baf1f9461ece62ac1a2ae67730b00c8.bundle\\";", +} +`; + +exports[`Options outputPath {String} with \`publicPath\` {String} without trailing slash 1`] = ` +Object { + "assets": Array [ + "output_path/9baf1f9461ece62ac1a2ae67730b00c8.bundle/library.json", + "output_path/9baf1f9461ece62ac1a2ae67730b00c8.bundle/swflite.bin", + ], + "source": "module.exports = \\"public_path/9baf1f9461ece62ac1a2ae67730b00c8.bundle\\";", +} +`; + +exports[`Options outputPath {String} without trailing slash 1`] = ` +Object { + "assets": Array [ + "output_path/9baf1f9461ece62ac1a2ae67730b00c8.bundle/library.json", + "output_path/9baf1f9461ece62ac1a2ae67730b00c8.bundle/swflite.bin", + ], + "source": "module.exports = __webpack_public_path__ + \\"output_path/9baf1f9461ece62ac1a2ae67730b00c8.bundle\\";", +} +`; diff --git a/test/options/__snapshots__/publicPath.test.js.snap b/test/options/__snapshots__/publicPath.test.js.snap new file mode 100644 index 0000000..871821a --- /dev/null +++ b/test/options/__snapshots__/publicPath.test.js.snap @@ -0,0 +1,41 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Options publicPath {Function} 1`] = ` +Object { + "assets": Array [ + "9baf1f9461ece62ac1a2ae67730b00c8.bundle/library.json", + "9baf1f9461ece62ac1a2ae67730b00c8.bundle/swflite.bin", + ], + "source": "module.exports = \\"public_path/9baf1f9461ece62ac1a2ae67730b00c8.bundle\\";", +} +`; + +exports[`Options publicPath {String} - URL 1`] = ` +Object { + "assets": Array [ + "9baf1f9461ece62ac1a2ae67730b00c8.bundle/library.json", + "9baf1f9461ece62ac1a2ae67730b00c8.bundle/swflite.bin", + ], + "source": "module.exports = \\"https://cdn.com/9baf1f9461ece62ac1a2ae67730b00c8.bundle\\";", +} +`; + +exports[`Options publicPath {String} - Without trailing slash 1`] = ` +Object { + "assets": Array [ + "9baf1f9461ece62ac1a2ae67730b00c8.bundle/library.json", + "9baf1f9461ece62ac1a2ae67730b00c8.bundle/swflite.bin", + ], + "source": "module.exports = \\"public_path/9baf1f9461ece62ac1a2ae67730b00c8.bundle\\";", +} +`; + +exports[`Options publicPath {String} 1`] = ` +Object { + "assets": Array [ + "9baf1f9461ece62ac1a2ae67730b00c8.bundle/library.json", + "9baf1f9461ece62ac1a2ae67730b00c8.bundle/swflite.bin", + ], + "source": "module.exports = \\"public_path/9baf1f9461ece62ac1a2ae67730b00c8.bundle\\";", +} +`; diff --git a/test/options/outputPath.test.js b/test/options/outputPath.test.js new file mode 100644 index 0000000..7a47529 --- /dev/null +++ b/test/options/outputPath.test.js @@ -0,0 +1,187 @@ +/* eslint-disable + prefer-destructuring, +*/ +import webpack from '../helpers/compiler'; + +describe('Options', () => { + describe('outputPath', () => { + test('{String}', async () => { + const config = { + loader: { + test: /\.swf$/, + options: { + outputPath: 'output_path/', + }, + }, + }; + + const stats = await webpack('fixture.js', config); + const { assets, source } = stats.toJson().modules[1]; + + expect({ assets, source }).toMatchSnapshot(); + }); + + test('{String} without trailing slash', async () => { + const config = { + loader: { + test: /\.swf$/, + options: { + outputPath: 'output_path', + }, + }, + }; + + const stats = await webpack('fixture.js', config); + const { assets, source } = stats.toJson().modules[1]; + + expect({ assets, source }).toMatchSnapshot(); + }); + + test('{String} with `options.name`', async () => { + const config = { + loader: { + test: /\.swf$/, + options: { + name: '[path][name].[ext]', + outputPath: 'output_path/', + }, + }, + }; + + const stats = await webpack('fixture.js', config); + const { assets, source } = stats.toJson().modules[1]; + + expect({ assets, source }).toMatchSnapshot(); + }); + + test('{Function}', async () => { + const config = { + loader: { + test: /\.swf$/, + options: { + outputPath(url) { + return `output_path_func/${url}`; + }, + }, + }, + }; + + const stats = await webpack('fixture.js', config); + const { assets, source } = stats.toJson().modules[1]; + + expect({ assets, source }).toMatchSnapshot(); + }); + + test('{Function} with `options.name`', async () => { + const config = { + loader: { + test: /\.swf$/, + options: { + name: '[name].[ext]', + outputPath(url) { + return `output_path_func/${url}`; + }, + }, + }, + }; + + const stats = await webpack('fixture.js', config); + const { assets, source } = stats.toJson().modules[1]; + + expect({ assets, source }).toMatchSnapshot(); + }); + + test('{String} with `publicPath` {String}', async () => { + const config = { + loader: { + test: /\.swf$/, + options: { + outputPath: 'output_path/', + publicPath: 'public_path/', + }, + }, + }; + + const stats = await webpack('fixture.js', config); + const { assets, source } = stats.toJson().modules[1]; + + expect({ assets, source }).toMatchSnapshot(); + }); + + test('{String} with `publicPath` {String} without trailing slash', async () => { + const config = { + loader: { + test: /\.swf$/, + options: { + outputPath: 'output_path', + publicPath: 'public_path', + }, + }, + }; + + const stats = await webpack('fixture.js', config); + const { assets, source } = stats.toJson().modules[1]; + + expect({ assets, source }).toMatchSnapshot(); + }); + + test('{Function} with `publicPath` {String}', async () => { + const config = { + loader: { + test: /\.swf$/, + options: { + publicPath: 'public_path/', + outputPath(url) { + return `output_path_func/${url}`; + }, + }, + }, + }; + + const stats = await webpack('fixture.js', config); + const { assets, source } = stats.toJson().modules[1]; + + expect({ assets, source }).toMatchSnapshot(); + }); + + test('{String} with `publicPath` {Function}', async () => { + const config = { + loader: { + test: /\.swf$/, + options: { + outputPath: 'output_path/', + publicPath(url) { + return `public_path_func/${url}`; + }, + }, + }, + }; + + const stats = await webpack('fixture.js', config); + const { assets, source } = stats.toJson().modules[1]; + + expect({ assets, source }).toMatchSnapshot(); + }); + + test('{Function} with `publicPath` {Function}', async () => { + const config = { + loader: { + test: /\.swf$/, + options: { + outputPath(url) { + return `output_path_func/${url}`; + }, + publicPath(url) { + return `public_path_func/${url}`; + }, + }, + }, + }; + + const stats = await webpack('fixture.js', config); + const { assets, source } = stats.toJson().modules[1]; + + expect({ assets, source }).toMatchSnapshot(); + }); + }); +}); diff --git a/test/options/publicPath.test.js b/test/options/publicPath.test.js new file mode 100644 index 0000000..728625b --- /dev/null +++ b/test/options/publicPath.test.js @@ -0,0 +1,74 @@ +/* eslint-disable + prefer-destructuring, +*/ +import webpack from '../helpers/compiler'; + +describe('Options', () => { + describe('publicPath', () => { + test('{String}', async () => { + const config = { + loader: { + test: /\.swf$/, + options: { + publicPath: 'public_path/', + }, + }, + }; + + const stats = await webpack('fixture.js', config); + const { assets, source } = stats.toJson().modules[1]; + + expect({ assets, source }).toMatchSnapshot(); + }); + + test('{String} - Without trailing slash', async () => { + const config = { + loader: { + test: /\.swf$/, + options: { + publicPath: 'public_path', + }, + }, + }; + + const stats = await webpack('fixture.js', config); + const { assets, source } = stats.toJson().modules[1]; + + expect({ assets, source }).toMatchSnapshot(); + }); + + test('{String} - URL', async () => { + const config = { + loader: { + test: /\.swf$/, + options: { + publicPath: 'https://cdn.com/', + }, + }, + }; + + const stats = await webpack('fixture.js', config); + const { assets, source } = stats.toJson().modules[1]; + + expect({ assets, source }).toMatchSnapshot(); + }); + + test('{Function}', async () => { + const config = { + loader: { + test: /\.swf$/, + options: { + publicPath(url) { + return `public_path/${url}`; + }, + }, + }, + }; + + const stats = await webpack('fixture.js', config); + const { assets, source } = stats.toJson().modules[1]; + + expect({ assets, source }).toMatchSnapshot(); + }); + }); +}); \ No newline at end of file