Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/generate loader #183

Merged
merged 25 commits into from
Sep 29, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
63b3660
Add template files for loader yeoman generator
ianjsikes Sep 4, 2017
14701f8
Create yeoman generator for a webpack loader project
ianjsikes Sep 4, 2017
4349ba7
Add tests for loader-generator
ianjsikes Sep 4, 2017
b633b14
Add `mkdirp` dependency for loader-generator
ianjsikes Sep 4, 2017
9de21fa
Add function to create yeoman env and run loader-generator
ianjsikes Sep 4, 2017
b6972a7
Add `generate-loader` command to webpack-cli
ianjsikes Sep 4, 2017
91ab4ea
Copy loader templates from proper directory
ianjsikes Sep 4, 2017
6a3e6a6
Add template files for plugin generator
ianjsikes Sep 4, 2017
1aa6d30
Create yeoman generator for webpack plugins
ianjsikes Sep 4, 2017
57fa953
Add function to create yeoman env and run plugin generator
ianjsikes Sep 4, 2017
d550206
Add cli command to generate plugin
ianjsikes Sep 4, 2017
88eeede
Register generate- commands in yargs
ianjsikes Sep 4, 2017
b73a24d
Add template files for loader examples and tests
ianjsikes Sep 5, 2017
0cb2f69
Copy loader test and example template files in generator
ianjsikes Sep 5, 2017
7d6fd90
Add template files for plugin examples and tests
ianjsikes Sep 5, 2017
4aa82f2
Copy plugin test and example template files in generator
ianjsikes Sep 5, 2017
bb164a8
Refactor generator file copying, switch .includes with .indexOf in CL…
ianjsikes Sep 8, 2017
10ce3c2
Change `indexOf('foo') > -1` to `indexOf('foo') >= 0`
ianjsikes Sep 11, 2017
4a8f738
Factor out generator copy utilities into separate module
ianjsikes Sep 11, 2017
49c887e
Rewrite generators using a function that returns a customized generat…
ianjsikes Sep 11, 2017
e93c842
Merge branch 'master' into feature/generate-loader
ianjsikes Sep 15, 2017
a106d9d
Fix linting errors
ianjsikes Sep 15, 2017
09aef62
Merge branch 'master' into feature/generate-loader
ianjsikes Sep 18, 2017
41e2c9e
Remove //eslint-disable lines from template files
ianjsikes Sep 29, 2017
5930f37
Merge branch 'feature/generate-loader' of https://github.com/ianjsike…
ianjsikes Sep 29, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions bin/config-yargs.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ module.exports = function(yargs) {
"Migrate your webpack configuration from webpack 1 to webpack 2",
group: INIT_GROUP
},
"generate-loader": {
type: "boolean",
describe: "Generates a new webpack loader project",
group: INIT_GROUP
},
"generate-plugin": {
type: "boolean",
describe: "Generates a new webpack plugin project",
group: INIT_GROUP
},
config: {
type: "string",
describe: "Path to the config file",
Expand Down
8 changes: 6 additions & 2 deletions bin/webpack.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,18 +159,22 @@ if (argv.verbose) {
argv["display-cached"] = true;
argv["display-cached-assets"] = true;
}
if (argv._.includes("init")) {
if (argv._.indexOf("init") >= 0) {
const initPkgs = argv._.length === 1 ? [] : [argv._.pop()];

return require("../lib/initialize")(initPkgs);
} else if (argv._.includes("migrate")) {
} else if (argv._.indexOf("migrate") >= 0) {
const filePaths = argv._.length === 1 ? [] : [argv._.pop()];
if (!filePaths.length) {
throw new Error("Please specify a path to your webpack config");
}
const inputConfigPath = path.resolve(process.cwd(), filePaths[0]);

return require("../lib/migrate.js")(inputConfigPath, inputConfigPath);
} else if (argv._.indexOf("generate-loader") >= 0) {
return require("../lib/generate-loader/index.js")();
} else if (argv._.indexOf("generate-plugin") >= 0) {
return require("../lib/generate-plugin/index.js")();
} else {
var options = require("./convert-argv")(yargs, argv);

Expand Down
17 changes: 17 additions & 0 deletions lib/generate-loader/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
var yeoman = require("yeoman-environment");
var LoaderGenerator = require("./loader-generator").LoaderGenerator;

/**
* Runs a yeoman generator to create a new webpack loader project
* @returns {void}
*/
function loaderCreator() {
var env = yeoman.createEnv();
var generatorName = "webpack-loader-generator";

env.registerStub(LoaderGenerator, generatorName);

env.run(generatorName);
}

module.exports = loaderCreator;
58 changes: 58 additions & 0 deletions lib/generate-loader/loader-generator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
var path = require("path");
var _ = require("lodash");
var webpackGenerator = require("../utils/webpack-generator");

/**
* Formats a string into webpack loader format
* (eg: 'style-loader', 'raw-loader')
*
* @param {string} name A loader name to be formatted
* @returns {string} The formatted string
*/
function makeLoaderName(name) {
name = _.kebabCase(name);
if (!/loader$/.test(name)) {
name += "-loader";
}
return name;
}

/**
* A yeoman generator class for creating a webpack
* loader project. It adds some starter loader code
* and runs `webpack-defaults`.
*
* @class LoaderGenerator
* @extends {Generator}
*/
var LoaderGenerator = webpackGenerator(
[{
type: "input",
name: "name",
message: "Loader name",
default: "my-loader",
filter: makeLoaderName,
validate: str => str.length > 0,
}],
path.join(__dirname, "templates"),
[
"src/cjs.js.tpl",
"test/test-utils.js.tpl",
"test/unit.test.js.tpl",
"test/functional.test.js.tpl",
"test/fixtures/simple-file.js.tpl",
"examples/simple/webpack.config.js.tpl",
"examples/simple/src/index.js.tpl",
"examples/simple/src/lazy-module.js.tpl",
"examples/simple/src/static-esm-module.js.tpl",
],
[
"src/_index.js.tpl",
],
(gen) => ({ name: gen.props.name })
);

module.exports = {
makeLoaderName,
LoaderGenerator,
};
17 changes: 17 additions & 0 deletions lib/generate-loader/loader-generator.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"use strict";

var makeLoaderName = require("./loader-generator").makeLoaderName;

describe("makeLoaderName", () => {

it("should kebab-case loader name and append '-loader'", () => {
var loaderName = makeLoaderName("This is a test");
expect(loaderName).toEqual("this-is-a-test-loader");
});

it("should not modify a properly formatted loader name", () => {
var loaderName = makeLoaderName("properly-named-loader");
expect(loaderName).toEqual("properly-named-loader");
});

});
11 changes: 11 additions & 0 deletions lib/generate-loader/templates/examples/simple/src/index.js.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import esmModule from './static-esm-module';

const getLazyModule = () => System.import('./lazy-module');

setTimeout(() => {
getLazyModule.then((modDefault) => {
console.log(modDefault);
});
}, 300);

console.log(esmModule);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default 'lazy';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default 'foo';
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const path = require('path');

module.exports = {
entry: './src/index.js',
output: {
path: path.join(__dirname, 'example_dist'),
filename: '[name].chunk.js',
},
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: 'example-loader',
options: {},
},
],
},
],
},
resolveLoader: {
alias: {
'example-loader': require.resolve('../../src/'),
},
},
};
25 changes: 25 additions & 0 deletions lib/generate-loader/templates/src/_index.js.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* See the webpack docs for more information about loaders:
* https://github.com/webpack/docs/wiki/how-to-write-a-loader
*/

export default function loader(source) {
const { loaders, resource, request, version, webpack } = this;

const newSource = `
/**
* <%= name %>
*
* Resource Location: ${resource}
* Loaders chained to module: ${JSON.stringify(loaders)}
* Loader API Version: ${version}
* Is this in "webpack mode": ${webpack}
* This is the users request for the module: ${request}
*/
/**
* Original Source From Loader
*/
${source}`;

return newSource;
}
1 change: 1 addition & 0 deletions lib/generate-loader/templates/src/cjs.js.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./index').default;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import foo from "./foo"; // eslint-disable-line

console.log(foo);
21 changes: 21 additions & 0 deletions lib/generate-loader/templates/test/functional.test.js.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {
runWebpackExampleInMemory,
} from '../test/test-utils';

test('should run with no errors or warnings', async () => {
const buildStats = await runWebpackExampleInMemory('simple');
const { errors, warnings } = buildStats;

expect([...errors, ...warnings].length).toBe(0);
});

test('should append transformations to JavaScript module', async () => {
const buildStats = await runWebpackExampleInMemory('simple');
const { modules } = buildStats;

const moduleToTest = modules[0].source()._source._value;
const loadedString = '* Original Source From Loader';

expect(moduleToTest).toEqual(expect.stringContaining(loadedString));
expect(moduleToTest).toMatchSnapshot();
});
82 changes: 82 additions & 0 deletions lib/generate-loader/templates/test/test-utils.js.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import path from 'path';
import webpack from 'webpack';
import Promise from 'bluebird';
import MemoryFs from 'memory-fs';

const fs = new MemoryFs();
const unitTestFixtures = path.resolve(__dirname, 'fixtures');

/**
*
*
* @param {string} fixtureName
* @param {string} [withQueryString='']
* @returns {string} Absolute path of a file with query that is to be run by a loader.
*/
function getFixtureResource(fixtureName, withQueryString = '') {
return `${getFixture(fixtureName)}?${withQueryString}`;
}

/**
*
*
* @param {string} fixtureName
* @returns {string} Absolute path of a file with query that is to be run by a loader.
*/
function getFixture(fixtureName) {
return path.resolve(unitTestFixtures, `${fixtureName}.js`);
}

/**
*
*
* @param {Object} withOptions - Loader Options
* @returns {{loader: string, options: Object}}
*/
function getLoader(withOptions) {
return [{ loader: path.resolve(__dirname, '../dist/index.js'), options: withOptions }];
}

/**
*
*
* @param {string} exampleName
* @returns {Object|Array} - Returns an object or array of objects representing the webpack configuration options
*/
function getExampleConfig(exampleName) {
return require(`../examples/${exampleName}/webpack.config.js`);
}

/**
*
*
* @param {string} exampleName - name of example inside of examples folder
* @returns
*/
async function runWebpackExampleInMemory(exampleName) {
const webpackConfig = getExampleConfig(exampleName);
const compiler = webpack(webpackConfig);

compiler.outputFileSystem = fs;

const run = Promise.promisify(compiler.run, { context: compiler });
const stats = await run();


const { compilation } = stats;
const { errors, warnings, assets, entrypoints, chunks, modules } = compilation;
const statsJson = stats.toJson();

return {
assets,
entrypoints,
errors,
warnings,
stats,
chunks,
modules,
statsJson,
};
}

export { getExampleConfig, runWebpackExampleInMemory, fs, getFixtureResource, getLoader, getFixture };
32 changes: 32 additions & 0 deletions lib/generate-loader/templates/test/unit.test.js.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import fs from 'fs';
import Promise from 'bluebird';
import { runLoaders } from 'loader-runner';
import { getFixtureResource, getFixture, getLoader } from './test-utils';

const runLoadersPromise = Promise.promisify(runLoaders);
const readFilePromise = Promise.promisify(fs.readFile, { context: fs });


const loaders = getLoader();

describe('Example Loader Tests: Fixture: simple-file', () => {
const fixtureName = 'simple-file';
const resource = getFixture(fixtureName);

test('loaded file should be different', async () => {
const originalSource = await readFilePromise(resource);
const { result } = await runLoadersPromise({ resource: getFixtureResource(fixtureName), loaders });

expect(result).not.toEqual(originalSource);
});

test('loader prepends correct information', async () => {
const { result } = await runLoadersPromise({ resource: getFixtureResource(fixtureName), loaders });
const resultMatcher = expect.arrayContaining([
expect.stringContaining(' * Original Source From Loader'),
]);

expect(result).toEqual(resultMatcher);
expect(result).toMatchSnapshot();
});
});
17 changes: 17 additions & 0 deletions lib/generate-plugin/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
var yeoman = require("yeoman-environment");
var PluginGenerator = require("./plugin-generator").PluginGenerator;

/**
* Runs a yeoman generator to create a new webpack plugin project
* @returns {void}
*/
function pluginCreator() {
var env = yeoman.createEnv();
var generatorName = "webpack-plugin-generator";

env.registerStub(PluginGenerator, generatorName);

env.run(generatorName);
}

module.exports = pluginCreator;
Loading