Support WebPack 5

pull/1/head
WolverinDEV 2021-03-16 19:20:57 +01:00
parent 3aede0bc1a
commit 73f07f6bea
13 changed files with 2390 additions and 5786 deletions

2
demo/.gitignore vendored
View File

@ -3,3 +3,5 @@ node_modules
app/*.js app/*.js
app/*.js.map app/*.js.map
dist-plugin/

9
demo/app/test.d.ts vendored
View File

@ -2,14 +2,13 @@
* DO NOT MODIFY THIS FILE! * DO NOT MODIFY THIS FILE!
* *
* This file has been auto generated by the svg-sprite generator. * This file has been auto generated by the svg-sprite generator.
* Sprite source directory: D:\git\web\webpack-svg-sprite\demo\sprites * Sprite source directory: D:\__on_g__git\web\webpack-svg-sprite\demo\sprites
* Sprite count: 21 * Sprite count: 22
*/ */
declare module "svg-sprites/test" { declare module "svg-sprites/test" {
export type TestIconClasses = "client-_player_commander_on" | "client-about" | "client-activate_microphone" | "client-add" | "client-add_foe" | "client-add_folder" | "client-add_friend" | "client-addon-collection" | "client-addon" | "client-apply" | "client-arrow_down" | "client-arrow_left" | "client-arrow_right" | "client-arrow_up" | "client-away" | "client-ban_client" | "client-ban_list" | "client-bookmark_add" | "client-bookmark_add_folder" | "client-bookmark_duplicate" | "client-w2g"; export type TestIconClasses = "client-about" | "client-activate_microphone" | "client-add" | "client-add_foe" | "client-add_folder" | "client-add_friend" | "client-addon-collection" | "client-addon" | "client-apply" | "client-arrow_down" | "client-arrow_left" | "client-arrow_right" | "client-arrow_up" | "client-away" | "client-ban_client" | "client-ban_list" | "client-bookmark_add" | "client-bookmark_add_folder" | "client-bookmark_duplicate" | "client-channel_popin" | "client-player_off" | "client-w2g";
export enum TestIcons { export enum TestIcons {
PlayerCommanderOn = "client-_player_commander_on",
About = "client-about", About = "client-about",
ActivateMicrophone = "client-activate_microphone", ActivateMicrophone = "client-activate_microphone",
Add = "client-add", Add = "client-add",
@ -29,6 +28,8 @@ declare module "svg-sprites/test" {
BookmarkAdd = "client-bookmark_add", BookmarkAdd = "client-bookmark_add",
BookmarkAddFolder = "client-bookmark_add_folder", BookmarkAddFolder = "client-bookmark_add_folder",
BookmarkDuplicate = "client-bookmark_duplicate", BookmarkDuplicate = "client-bookmark_duplicate",
ChannelPopin = "client-channel_popin",
PlayerOff = "client-player_off",
W2g = "client-w2g", W2g = "client-w2g",
} }

4476
demo/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,27 +0,0 @@
{
"name": "svg-sprite-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/react": "^16.9.45",
"@types/react-dom": "^16.9.8",
"ts-loader": "^8.0.2",
"typescript": "^3.9.7",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12"
},
"dependencies": {
"fs": "0.0.1-security",
"fs-extra": "^9.0.1",
"html-webpack-plugin": "^4.3.0",
"react": "^16.13.1",
"react-dom": "^16.13.1"
}
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<svg id="client-channel_popin" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<g transform="matrix(.66666 0 0 .66666 0 .00016666)">
<path d="m23.707.293c-.391-.391-1.023-.391-1.414 0l-4.97 4.97-2.043-2.043c-.214-.215-.537-.279-.817-.163s-.463.39-.463.693v5.5c0 .414.336.75.75.75h5.5c.303 0 .577-.183.693-.463s.052-.603-.163-.817l-2.043-2.043 4.97-4.97c.391-.391.391-1.023 0-1.414z" fill="#fff"/>
</g>
<g transform="matrix(.66666 0 0 .66666 0 .00016666)">
<path d="m18 19v2c0 .55-.45 1-1 1h-14c-.55 0-1-.45-1-1v-13.95c-1.14.23-2 1.24-2 2.45v12c0 1.38 1.12 2.5 2.5 2.5h15c1.38 0 2.5-1.12 2.5-2.5v-2.5z" fill="#ccc"/>
</g>
<g transform="matrix(.66666 0 0 .66666 0 .00016666)">
<path d="m22 6.25v.87l.19.19c.79.79 1.03 1.96.6 2.99-.18.43-.45.79-.79 1.06v2.64c0 .55-.45 1-1 1h-14c-.55 0-1-.45-1-1v-9h6v-1.25c0-1.12.67-2.11 1.7-2.54s2.21-.19 3 .6l.62.62 2.44-2.43h-13.26c-1.38 0-2.5 1.12-2.5 2.5v12c0 1.38 1.12 2.5 2.5 2.5h15c1.38 0 2.5-1.12 2.5-2.5v-10.26z" fill="#7289da"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1,8 +1,8 @@
import * as path from "path"; import * as path from "path";
import {Configuration} from "webpack"; import {Configuration} from "webpack";
import {CleanWebpackPlugin} from "clean-webpack-plugin"; import {CleanWebpackPlugin} from "clean-webpack-plugin";
import * as SpriteGenerator from "../plugin"; import HtmlWebpackPlugin from "html-webpack-plugin";
import * as HtmlWebpackPlugin from "html-webpack-plugin"; import * as SpriteGenerator from "../plugin/";
export = { export = {
entry: path.join(__dirname, "app", "index.tsx"), entry: path.join(__dirname, "app", "index.tsx"),
@ -47,18 +47,23 @@ export = {
rules: [ rules: [
{ {
test: /\.tsx?$/, test: /\.tsx?$/,
loader: [ use: [
"ts-loader" "ts-loader"
] ]
} }
] ]
}, },
mode: process.env.NODE_ENV === "development" ? "development" : "production", mode: process.env.NODE_ENV === "development" ? "development" : "production",
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 9000,
},
resolve: { resolve: {
extensions: [".ts", ".tsx", ".css", ".js"] extensions: [".ts", ".tsx", ".css", ".js"]
}, },
output: { output: {
filename: "[name].[contenthash].js", filename: "[name].[contenthash].js",
path: path.resolve(__dirname, "dist")
} }
} as Configuration; } as Configuration;

3503
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,13 @@
{ {
"name": "webpack-svg-sprite-generator", "name": "webpack-svg-sprite-generator",
"version": "1.0.17", "version": "5.0.0",
"description": "", "description": "",
"main": "plugin.js", "main": "plugin.js",
"types": "ts/index.d.ts", "types": "ts/index.d.ts",
"scripts": { "scripts": {
"build": "webpack" "build": "webpack",
"build-demo": "webpack --config demo/webpack.config.js",
"serve-demo": "webpack-dev-server --config demo/webpack.config.js"
}, },
"keywords": [], "keywords": [],
"author": "WolverinDEV", "author": "WolverinDEV",
@ -18,21 +20,28 @@
"type": "git" "type": "git"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^17.0.3",
"@types/react-dom": "^17.0.2",
"@types/tapable": "^1.0.6", "@types/tapable": "^1.0.6",
"@types/webpack": "^4.41.21", "@types/webpack": "^4.41.21",
"@webpack-cli/serve": "^1.3.0",
"change-case": "^4.1.1", "change-case": "^4.1.1",
"clean-webpack-plugin": "^3.0.0", "clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^6.0.3", "copy-webpack-plugin": "^6.0.3",
"html-webpack-plugin": "^5.3.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"sha1": "^1.1.1", "sha1": "^1.1.1",
"ts-loader": "^8.0.2", "ts-loader": "^8.0.2",
"typescript": "^3.9.7", "typescript": "^3.9.7",
"webpack-cli": "^3.3.12", "webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.2",
"xml-parser": "^1.2.1" "xml-parser": "^1.2.1"
}, },
"dependencies": { "dependencies": {
"path": "^0.12.7",
"webpack": "^4.44.1",
"fs-extra": "^9.0.1", "fs-extra": "^9.0.1",
"potpack": "^1.0.1" "path": "^0.12.7",
"potpack": "^1.0.1",
"webpack": "^5.26.1"
} }
} }

View File

@ -1,11 +1,9 @@
import * as fs from "fs-extra"; import * as fs from "fs-extra";
import * as path from "path"; import * as path from "path";
import * as XMLParser from "xml-parser";
import { pascalCase } from "change-case"; import { pascalCase } from "change-case";
let potpack = require("potpack"); import XMLParser from "xml-parser";
if(typeof potpack !== "function" && potpack.default) import potpack from "potpack";
potpack = potpack.default;
function generateAttributes(attributes: XMLParser.Attributes) { function generateAttributes(attributes: XMLParser.Attributes) {
const keys = Object.keys(attributes); const keys = Object.keys(attributes);
@ -50,7 +48,7 @@ export async function generateSpriteSvg(sprite: GeneratedSprite) {
let result = ""; let result = "";
result += `<?xml version="1.0" encoding="utf-8"?>\n`; result += `<?xml version="1.0" encoding="utf-8"?>\n`;
result += `<!-- ${sprite.entries.length} icons packed -->\n`; result += `<!-- ${sprite.entries.length} icons packed -->\n`;
result += `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${sprite.width}" height="${sprite.height}" viewBox="0 0 ${sprite.width} ${sprite.height}">\n`; result += `<svg xmlns="http://www.w3.org/2000/svg" width="${sprite.width}" height="${sprite.height}" viewBox="0 0 ${sprite.width} ${sprite.height}">\n`;
for(const file of sprite.entries) { for(const file of sprite.entries) {
const root = file.data.root; const root = file.data.root;
@ -252,13 +250,13 @@ export async function generateSpriteJs(options: SpriteDtsOptions, sprite: Genera
lines.push(`let ClassList = [${sprite.entries.map(e => `"${cssClassPrefix}${e.name}", `)}];`); lines.push(`let ClassList = [${sprite.entries.map(e => `"${cssClassPrefix}${e.name}", `)}];`);
lines.push(``); lines.push(``);
lines.push(`Object.defineProperty(exports, "__esModule", { value: true });`); lines.push(`Object.defineProperty(module.exports, "__esModule", { value: true });`);
lines.push(`exports.${options.enumName} = Object.freeze(EnumClassList);`); lines.push(`module.exports.${options.enumName} = Object.freeze(EnumClassList);`);
lines.push(`exports.spriteUrl = SpriteUrl;`); lines.push(`module.exports.spriteUrl = SpriteUrl;`);
lines.push(`exports.classList = Object.freeze(ClassList);`); lines.push(`module.exports.classList = Object.freeze(ClassList);`);
lines.push(`exports.spriteEntries = Object.freeze(SpriteEntries);`); lines.push(`module.exports.spriteEntries = Object.freeze(SpriteEntries);`);
lines.push(`exports.spriteWidth = ${sprite.width};`); lines.push(`module.exports.spriteWidth = ${sprite.width};`);
lines.push(`exports.spriteHeight = ${sprite.height};`); lines.push(`module.exports.spriteHeight = ${sprite.height};`);
return lines.join("\n"); return lines.join("\n");
} }
@ -291,7 +289,7 @@ export async function generateSprite(files: string[]) : Promise<GeneratedSprite>
} }
const rootAttributes = svg.data.root.attributes; const rootAttributes = svg.data.root.attributes;
const [ xOff, yOff, width, height ] = rootAttributes["viewBox"].split(" ").map(parseFloat); const [ /* xOff */, /* yOff */, width, height ] = rootAttributes["viewBox"].split(" ").map(parseFloat);
if(isNaN(width) || isNaN(height)) { if(isNaN(width) || isNaN(height)) {
console.warn("Skipping SVG %s because of invalid bounds (Parsed: %d x %d, Values: %o).", file, width, height, rootAttributes["viewBox"].split(" ")); console.warn("Skipping SVG %s because of invalid bounds (Parsed: %d x %d, Values: %o).", file, width, height, rootAttributes["viewBox"].split(" "));

View File

@ -1,7 +1,6 @@
import * as webpack from "webpack";
import * as path from "path"; import * as path from "path";
import * as fs from "fs-extra"; import * as fs from "fs-extra";
import * as sha1 from "sha1"; import sha1 from "sha1";
import { import {
GeneratedSprite, GeneratedSprite,
@ -12,6 +11,12 @@ import {
SpriteDtsOptions SpriteDtsOptions
} from "./generator"; } from "./generator";
const Module = require("webpack/lib/Module");
const RuntimeGlobals = require("webpack/lib/RuntimeGlobals");
const { RawSource } = require("webpack-sources");
import {Compiler} from "webpack";
export interface Options { export interface Options {
modulePrefix?: string, /* defaults to svg-sprites/ */ modulePrefix?: string, /* defaults to svg-sprites/ */
dtsOutputFolder: string, dtsOutputFolder: string,
@ -20,8 +25,6 @@ export interface Options {
} }
} }
const Module = require("webpack/lib/Module");
const { RawSource } = require("webpack-sources");
interface SvgSpriteConfiguration { interface SvgSpriteConfiguration {
folder: string; folder: string;
@ -31,34 +34,47 @@ interface SvgSpriteConfiguration {
cssOptions: SpriteCssOptions[]; cssOptions: SpriteCssOptions[];
} }
const TYPES = new Set(["javascript"]);
const RUNTIME_REQUIREMENTS = new Set([
RuntimeGlobals.module
]);
class SvgSpriteModule extends Module { class SvgSpriteModule extends Module {
private readonly pluginConfig: Options; private readonly pluginConfig: Options;
private readonly configName: string; private readonly configName: string;
private readonly config: SvgSpriteConfiguration; private readonly config: SvgSpriteConfiguration;
private sprite: GeneratedSprite; private sprite: GeneratedSprite;
private spriteSvg: string; private spriteSvg: string;
private spriteCss: string; private spriteCss: string;
private spriteJs: string; private spriteJs: string;
private spriteAssetName: string; private spriteAssetName: string;
private spriteAssetUrl: string; private spriteAssetUrl: string;
constructor(context: string, pluginConfig: Options, configName: string, config: SvgSpriteConfiguration) { constructor(context: string, pluginConfig: Options, configName: string, config: SvgSpriteConfiguration) {
super("javascript/dynamic", context); super("javascript/dynamic", null);
this.pluginConfig = pluginConfig; this.pluginConfig = pluginConfig;
this.configName = configName; this.configName = configName;
this.config = config; this.config = config;
this.buildInfo = {};
this.clearDependenciesAndBlocks();
}
getSourceTypes() {
return TYPES;
} }
identifier() { identifier() {
return "svg-sprite/" + this.configName;
}
readableIdentifier() {
return `SVG sprite ` + this.configName; return `SVG sprite ` + this.configName;
} }
readableIdentifier(requestShortener) { needBuild(context, callback) {
return `SVG sprite ` + this.configName; callback(null, !this.buildMeta);
}
needRebuild() {
return false;
} }
build(options, compilation, resolver, fs_, callback) { build(options, compilation, resolver, fs_, callback) {
@ -71,7 +87,7 @@ class SvgSpriteModule extends Module {
}; };
this.buildInfo = { this.buildInfo = {
cacheable: true, cacheable: true,
assets: {} assets: {},
}; };
if(this.spriteAssetName) { if(this.spriteAssetName) {
@ -84,7 +100,7 @@ class SvgSpriteModule extends Module {
this.spriteSvg = await generateSpriteSvg(this.sprite); this.spriteSvg = await generateSpriteSvg(this.sprite);
this.spriteAssetName = "sprite-" + sha1(this.spriteSvg).substr(-20) + ".svg"; this.spriteAssetName = "sprite-" + sha1(this.spriteSvg).substr(-20) + ".svg";
this.spriteAssetUrl = (compilation.options.output.publicPath || "") + this.spriteAssetName; this.spriteAssetUrl = this.spriteAssetName;
this.buildInfo.assets[this.spriteAssetName] = new RawSource(this.spriteSvg); this.buildInfo.assets[this.spriteAssetName] = new RawSource(this.spriteSvg);
@ -105,7 +121,9 @@ class SvgSpriteModule extends Module {
}); });
} }
source() { codeGeneration(context) {
const sources = new Map();
const encodedCss = this.spriteCss const encodedCss = this.spriteCss
.replace(/%/g, "%25") .replace(/%/g, "%25")
.replace(/"/g, "%22") .replace(/"/g, "%22")
@ -119,7 +137,9 @@ class SvgSpriteModule extends Module {
lines.push(``); lines.push(``);
lines.push(`/* initialize typescript objects */`); lines.push(`/* initialize typescript objects */`);
lines.push(...this.spriteJs.split("\n")); lines.push(...this.spriteJs.split("\n"));
return new RawSource(lines.join("\n"));
sources.set("javascript", new RawSource(lines.join("\n")));
return { sources: sources, runtimeRequirements: RUNTIME_REQUIREMENTS };
} }
size() { size() {
@ -133,8 +153,11 @@ class SvgSpriteModule extends Module {
hash.update(this.spriteSvg || "none"); hash.update(this.spriteSvg || "none");
super.updateHash(hash, chunkGraph); super.updateHash(hash, chunkGraph);
} }
addReason(_requestModule, _dependency) { }
} }
export class SpriteGenerator { export class SpriteGenerator {
readonly options: Options; readonly options: Options;
constructor(options: Options) { constructor(options: Options) {
@ -143,20 +166,19 @@ export class SpriteGenerator {
this.options.modulePrefix = this.options.modulePrefix || "svg-sprites/"; this.options.modulePrefix = this.options.modulePrefix || "svg-sprites/";
} }
apply(compiler: webpack.Compiler) { apply(compiler: Compiler) {
compiler.hooks.thisCompilation.tap("SpriteGenerator", (compilation, { normalModuleFactory }) => { compiler.hooks.normalModuleFactory.tap("SpriteGenerator", normalModuleFactory => {
normalModuleFactory.hooks.factory.tap("SpriteGenerator", factory => (data, callback) => { normalModuleFactory.hooks.resolve.tap("SpriteGenerator", resolveData => {
if(data.request.startsWith(this.options.modulePrefix)) { if(!resolveData.request.startsWith(this.options.modulePrefix)) {
const configName = data.request.substr(this.options.modulePrefix.length);
if(this.options.configurations[configName] === undefined) {
callback("Missing SVG configuration " + configName);
return;
}
callback(null, new SvgSpriteModule(data.request, this.options, configName, this.options.configurations[configName]));
return; return;
} }
factory(data, callback); const configName = resolveData.request.substr(this.options.modulePrefix.length);
if(!this.options.configurations[configName]) {
return;
}
return new SvgSpriteModule(resolveData.request, this.options, configName, this.options.configurations[configName]);
}); });
}); });
} }

View File

@ -5,7 +5,8 @@
"sourceMap": false, "sourceMap": false,
"declarationDir": "dist/ts", "declarationDir": "dist/ts",
"declarationMap": false, "declarationMap": false,
"declaration": true "declaration": true,
"esModuleInterop": true
}, },
"include": [ "include": [
"plugin" "plugin"

View File

@ -51,7 +51,7 @@ export = {
target: "node", target: "node",
output: { output: {
path: path.resolve(__dirname, "dist"), path: process.env.OUTPUT_PATH || path.resolve(__dirname, "dist"),
filename: "plugin.js", filename: "plugin.js",
libraryTarget: "umd", libraryTarget: "umd",