@open-xchange/vite-plugin-icon-sprite 1.1.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/helper.js DELETED
@@ -1,56 +0,0 @@
1
- import { dictEntries } from "@open-xchange/vite-helper/utils";
2
- import { resolvePath } from "@open-xchange/vite-helper/file";
3
- import { PluginHelper } from "@open-xchange/vite-helper";
4
- // class SpritePluginHelper ===================================================
5
- /**
6
- * Plugin helper for all icon file formats.
7
- *
8
- * @template OptionsT
9
- * Exact type of the options interface.
10
- */
11
- export class SpritePluginHelper extends PluginHelper {
12
- /** Resolved configuration options. */
13
- options;
14
- // constructor ------------------------------------------------------------
15
- constructor(config, options) {
16
- super({
17
- ...config,
18
- virtualPrefix: options.format,
19
- loggerPrefix: options.format,
20
- logLevelEnvVar: "PLUGIN_ICON_SPRITE_LOGLEVEL",
21
- cacheSrcFiles: [options.imagesPath + "/**/*." + options.format, options.mappingPath],
22
- });
23
- this.options = options;
24
- }
25
- // protected methods ------------------------------------------------------
26
- /**
27
- * Reads an icon mapping configuration file, and returns the entries as
28
- * array.
29
- *
30
- * @param path
31
- * The path to the icon mapping configuration file to be read.
32
- *
33
- * @returns
34
- * The entries of the configuration file.
35
- */
36
- async readIconMapping() {
37
- const schemaPath = resolvePath("./mapping-schema.json", import.meta.url);
38
- const mappingDict = await this.readConfig(this.options.mappingPath, { schema: schemaPath });
39
- // convert to array of `IconMappingEntry`
40
- const iconMapping = [];
41
- for (const [iconId, entry] of dictEntries(mappingDict)) {
42
- const iconDict = (typeof entry === "string") ? { "*": entry } : entry;
43
- for (const [languages, iconPath] of dictEntries(iconDict)) {
44
- for (const iconLang of languages.split(",")) {
45
- if (iconLang === "*") {
46
- iconMapping.push({ iconId, iconPath });
47
- }
48
- else {
49
- iconMapping.push({ iconId, iconPath, iconLang });
50
- }
51
- }
52
- }
53
- }
54
- return iconMapping;
55
- }
56
- }
package/dist/index.d.ts DELETED
@@ -1,19 +0,0 @@
1
- import type { Plugin } from "vite";
2
- import { type SvgSpritePluginOptions } from "./plugin-svg.js";
3
- import { type PngSpritePluginOptions } from "./plugin-png.js";
4
- /**
5
- * Configuration options for generating icon sprites from SVG or PNG source
6
- * images.
7
- */
8
- export type IconSpritePluginOptions = SvgSpritePluginOptions | PngSpritePluginOptions;
9
- /**
10
- * A plugin for Vite to generate an icon sprite file from multiple SVG or PNG
11
- * source files.
12
- *
13
- * @param options
14
- * Plugin configuration options.
15
- *
16
- * @returns
17
- * The plugin instance.
18
- */
19
- export default function iconSpritePlugin(options: IconSpritePluginOptions): Plugin;
package/dist/index.js DELETED
@@ -1,19 +0,0 @@
1
- import { svgSpritePlugin } from "./plugin-svg.js";
2
- import { pngSpritePlugin } from "./plugin-png.js";
3
- // plugin =====================================================================
4
- /**
5
- * A plugin for Vite to generate an icon sprite file from multiple SVG or PNG
6
- * source files.
7
- *
8
- * @param options
9
- * Plugin configuration options.
10
- *
11
- * @returns
12
- * The plugin instance.
13
- */
14
- export default function iconSpritePlugin(options) {
15
- switch (options.format) {
16
- case "svg": return svgSpritePlugin(import.meta.url, options);
17
- case "png": return pngSpritePlugin(import.meta.url, options);
18
- }
19
- }
@@ -1,97 +0,0 @@
1
- import type { Plugin } from "vite";
2
- import { type Dict } from "@open-xchange/vite-helper/utils";
3
- import { type SpritePluginBaseOptions } from "./helper.js";
4
- /**
5
- * Configuration options for generating PNG sprites from PNG source images.
6
- */
7
- export interface PngSpritePluginOptions extends SpritePluginBaseOptions<"png"> {
8
- /**
9
- * Module name for the CSS file (with ".css" extension). The generated CSS
10
- * markup can be imported from `"virtual:png/[cssName]"`.
11
- */
12
- cssName: string;
13
- /**
14
- * Base size of all icons, in CSS pixels.
15
- */
16
- cssIconSize: number;
17
- /**
18
- * Additional padding around all icons to be generated in the sprites, in
19
- * CSS pixels.
20
- *
21
- * @default 0
22
- */
23
- cssIconPadding?: number;
24
- /**
25
- * The CSS selector for a PNG icon element to be used in all generated CSS
26
- * rules.
27
- *
28
- * @default "i.png-icon"
29
- */
30
- cssIconSelector?: string;
31
- /**
32
- * Name of the root element's attribute containing the locale identifier.
33
- * Needed to generate CSS selectors for localized icons.
34
- *
35
- * @default "lang"
36
- */
37
- rootLocaleAttr?: string;
38
- /**
39
- * Specifies how to generate the sprite PNG files.
40
- *
41
- * - `"source"`: The source PNGs will be copied into the generated sprites
42
- * unmodified. They will contain three color channels and an alpha
43
- * channel.
44
- * - `"monochrome"`: The generated sprites will be converted to monochrome.
45
- * They will contain a gray channel and an alpha channel.
46
- * - `"alpha"`: Only the alpha channels of the source PNGs will be copied
47
- * into the generated sprites. They will contain a single gray channel
48
- * representing the original alpha channels.
49
- *
50
- * @default "source"
51
- */
52
- spriteColorType?: "source" | "monochrome" | "alpha";
53
- /**
54
- * Specifies how the sprites are supposed to be used in CSS rules.
55
- *
56
- * - `"background"`: The sprites will be attached via "background-image".
57
- * - `"mask"`: The sprites will be attached via "mask-image".
58
- *
59
- * All related CSS properties (e.g. "(background|mask)-position" etc.) will
60
- * be generated accordingly.
61
- *
62
- * @default "background"
63
- */
64
- spriteFillType?: "background" | "mask";
65
- /**
66
- * List of all icon sprites to be generated.
67
- *
68
- * - The keys of the dictionary are the module names of the PNG sprites
69
- * (with ".png" extension). The generated sprite PNG file can be imported
70
- * from `"virtual:png/[key]"`.
71
- * - "factor" is the scaling factor (a multiplier for "cssIconSize"). All
72
- * source PNG files must have the effective pixel size (`cssIconSize *
73
- * factor`).
74
- * - "src" specifies the pattern used to build the path of the source PNG
75
- * files. MUST contain the placeholder "[path]" that will be replaced
76
- * with the base paths contained in the icon mapping file referred in the
77
- * option "mappingFile".
78
- */
79
- sprites: Dict<{
80
- factor: number;
81
- src: string;
82
- }>;
83
- }
84
- /**
85
- * A plugin for Vite to generate PNG sprite files from multiple PNG source
86
- * files.
87
- *
88
- * @param index
89
- * URL of the index source file of this plugin.
90
- *
91
- * @param options
92
- * Plugin configuration options.
93
- *
94
- * @returns
95
- * The plugin instance.
96
- */
97
- export declare function pngSpritePlugin(index: string, options: PngSpritePluginOptions): Plugin;
@@ -1,237 +0,0 @@
1
- import { Jimp, PNGColorType } from "jimp";
2
- import { onceFn } from "@open-xchange/vite-helper/utils";
3
- import { Cache } from "@open-xchange/vite-helper/cache";
4
- import { SpritePluginHelper } from "./helper.js";
5
- // constants ==================================================================
6
- const FILL_VENDOR_PREFIXES = {
7
- background: [],
8
- mask: ["webkit"],
9
- };
10
- // functions ==================================================================
11
- /**
12
- * Generates a CSS length string in pixels (omits unit for zero).
13
- *
14
- * @param value
15
- * The length to be emitted.
16
- *
17
- * @returns
18
- * The passed length with "px" unit.
19
- */
20
- function px(value) {
21
- return value ? `${value}px` : "0";
22
- }
23
- // class PngSpritePluginHelper ================================================
24
- class PngSpritePluginHelper extends SpritePluginHelper {
25
- #parseMappingOnce;
26
- // constructor ------------------------------------------------------------
27
- constructor(index, options) {
28
- super({
29
- pluginIndex: index,
30
- virtualModules: [options.cssName, ...Object.keys(options.sprites)],
31
- }, {
32
- cssIconPadding: 0,
33
- cssIconSelector: "i.png-icon",
34
- rootLocaleAttr: "lang",
35
- spriteColorType: "source",
36
- spriteFillType: "background",
37
- ...options,
38
- });
39
- this.#parseMappingOnce = onceFn(() => this.#parseMapping());
40
- }
41
- // public methods ---------------------------------------------------------
42
- /**
43
- * Generates an ES source module with CSS markup code for all icons.
44
- *
45
- * @returns
46
- * The source module with CSS markup for all icons.
47
- */
48
- async generateCssMarkupModule() {
49
- // shortcuts to plugin options
50
- const { cssName } = this.options;
51
- // try to resolve cached version of generated file
52
- return await this.generateModule(cssName, async () => {
53
- this.info("generating CSS markup %f", cssName);
54
- // parse mapping file which collects icon paths, CSS selectors, and entry positions
55
- const cssMarkup = (await this.#parseMappingOnce()).cssMarkup;
56
- this.info("CSS markup %f generated successfully", cssName);
57
- return cssMarkup;
58
- });
59
- }
60
- /**
61
- * Generates the ES source module for a PNG sprite with a specific scaling
62
- * factor.
63
- *
64
- * @param spriteName
65
- * The name of the sprite.
66
- *
67
- * @param factor
68
- * The scaling factor.
69
- *
70
- * @param srcPattern
71
- * The pattern for the source PNG images.
72
- *
73
- * @returns
74
- * The source module exporting the PNG sprite as base-64 encoded data URL.
75
- */
76
- async generateSpriteModule(spriteName, factor, srcPattern) {
77
- // shortcuts to plugin options
78
- const { imagesPath, cssIconSize, cssIconPadding, spriteColorType } = this.options;
79
- // check configuration
80
- this.ensure(factor > 0, "invalid scaling factor in configuration for sprite %f", spriteName);
81
- this.ensure(srcPattern.includes("[path]"), "placeholder [path] expected in configuration for sprite %f", spriteName);
82
- // try to resolve cached version of generated file
83
- return await this.generateModule(spriteName, async () => {
84
- this.info("generating sprite %f", spriteName);
85
- // parse mapping file which collects icon paths, CSS selectors, and entry positions
86
- const { cssSpriteWidth, cssSpriteHeight, entries } = await this.#parseMappingOnce();
87
- // new image data is not clean out-of-the-box, explicitly fill with zeros
88
- const sprite = new Jimp({ width: cssSpriteWidth * factor, height: cssSpriteHeight * factor, color: 0 });
89
- // process all entries in the mapping configuration
90
- for (const { iconPath, x, y } of entries) {
91
- // expected pixel size for the current scaling factor
92
- const size = cssIconSize * factor;
93
- // load and parse the source image file
94
- const path = imagesPath + "/" + srcPattern.replace("[path]", iconPath);
95
- const buffer = await this.readBuffer(path);
96
- const image = await Jimp.fromBuffer(buffer);
97
- // validate the image size
98
- const { width, height } = image.bitmap;
99
- this.ensure((width === size) && (height === size), "wrong image width in %f (expected %s but got %s)", path, `${size}x${size}`, `${width}x${height}`);
100
- // insert source image into the sprite
101
- sprite.blit({ src: image, x: (x + cssIconPadding) * factor, y: (y + cssIconPadding) * factor });
102
- }
103
- // copy alpha of all pixels to RGB channels (alpha channel will be exported as greyscale without alpha)
104
- if (spriteColorType === "alpha") {
105
- for (let d = sprite.bitmap.data, i = 0, l = d.length; i < l; i += 4) {
106
- d.fill(d[i + 3], i, i + 3); // copy A to RGB
107
- d[i + 3] = 255; // set A to full opacity
108
- }
109
- }
110
- // generate the binary PNG data (no preset constants for PNG color types in Jimp)
111
- const colorType = {
112
- source: PNGColorType.COLOR_ALPHA,
113
- monochrome: PNGColorType.GRAYSCALE_ALPHA,
114
- alpha: PNGColorType.GRAYSCALE,
115
- }[spriteColorType];
116
- const spriteBuffer = await sprite.getBuffer("image/png", { colorType });
117
- // convert to base64 encoded data URL
118
- const spriteDataUrl = `data:image/png;base64,${spriteBuffer.toString("base64")}`;
119
- const moduleSource = `export default ${JSON.stringify(spriteDataUrl)};`;
120
- this.info("sprite %f generated successfully", spriteName);
121
- return moduleSource;
122
- });
123
- }
124
- // private methods --------------------------------------------------------
125
- // parses the mapping file once, generates a map from short icon paths to `SpriteEntry` descriptor objects
126
- async #parseMapping() {
127
- // shortcuts to plugin options
128
- const { cssIconSize, cssIconPadding, cssIconSelector, rootLocaleAttr, spriteFillType } = this.options;
129
- // CSS selectors for all sprite entries, mapped by short icon path (different icons may refer to the same source PNG)
130
- const selectorMap = new Cache();
131
- // process all entries in the mapping configuration, collect CSS selectors for each entry in the sprite
132
- for (const { iconId, iconPath, iconLang } of await this.readIconMapping()) {
133
- const selectors = selectorMap.upsert(iconPath, () => []);
134
- const selector = `${cssIconSelector}[data-icon-id="${iconId}"]`;
135
- if (iconLang) {
136
- selectors.push(`:root[${rootLocaleAttr}="${iconLang}"] ${selector}`);
137
- selectors.push(`:root[${rootLocaleAttr}^="${iconLang}_"] ${selector}`);
138
- }
139
- else {
140
- selectors.push(selector);
141
- }
142
- }
143
- // the size of one icon occupied in the sprite including padding, for scaling factor 1
144
- const cssTileSize = cssIconSize + 2 * cssIconPadding;
145
- // number of distinct icons in the sprites
146
- const iconCount = selectorMap.size;
147
- // generate a square-shaped sprite
148
- const rowLength = Math.ceil(Math.sqrt(iconCount));
149
- // the resulting size of a sprite, for scaling factor 1
150
- const cssSpriteWidth = cssTileSize * rowLength;
151
- const cssSpriteHeight = cssTileSize * Math.ceil(iconCount / rowLength);
152
- // additional vendor prefixes for background/fill attributes
153
- const prefixes = FILL_VENDOR_PREFIXES[spriteFillType];
154
- // generates a background/mask CSS property definition with correct vendor prefixes
155
- const fillProp = (name, value) => {
156
- const prop = `${spriteFillType}-${name}: ${value};`;
157
- return prefixes.map(prefix => `-${prefix}-${prop} `).join("") + prop;
158
- };
159
- // the contents of the resulting CSS file with icon definitions
160
- const cssLines = [
161
- `${cssIconSelector} {`,
162
- " display: inline-block;",
163
- ` width: ${px(cssTileSize)};`,
164
- ` height: ${px(cssTileSize)};`,
165
- " flex: 0 0 auto;",
166
- ` ${fillProp("size", `${px(cssSpriteWidth)} ${px(cssSpriteHeight)}`)}`,
167
- ` ${fillProp("origin", "content-box")}`,
168
- "}",
169
- `${cssIconSelector}[data-icon-id="none"] { visibility: hidden; }`,
170
- ];
171
- // generate all sprite entries as an array
172
- const entries = Array.from(selectorMap, ([iconPath, selectors], index) => {
173
- const x = cssTileSize * (index % rowLength);
174
- const y = cssTileSize * Math.floor(index / rowLength);
175
- const prop = fillProp("position", `${px(-x)} ${px(-y)}`);
176
- for (const selector of selectors) {
177
- cssLines.push(`${selector} { ${prop} }`);
178
- }
179
- return { iconPath, x, y };
180
- });
181
- // create the resulting CSS markup as string
182
- const cssMarkup = cssLines.join("\n") + "\n";
183
- return { cssMarkup, cssSpriteWidth, cssSpriteHeight, entries };
184
- }
185
- }
186
- // plugin =====================================================================
187
- /**
188
- * A plugin for Vite to generate PNG sprite files from multiple PNG source
189
- * files.
190
- *
191
- * @param index
192
- * URL of the index source file of this plugin.
193
- *
194
- * @param options
195
- * Plugin configuration options.
196
- *
197
- * @returns
198
- * The plugin instance.
199
- */
200
- export function pngSpritePlugin(index, options) {
201
- // resolved configuration options
202
- const { cssName, sprites } = options;
203
- // helper instance for file system access, logging, etc.
204
- const helper = new PngSpritePluginHelper(index, options);
205
- // create and return the plugin object
206
- return {
207
- name: "@open-xchange/vite-plugin-icon-sprite/png",
208
- // initialize file system cache for generated modules
209
- async configResolved(viteConfig) {
210
- await helper.initializeCache(viteConfig);
211
- },
212
- // pick matching imports
213
- resolveId(source) {
214
- return helper.resolveVirtualModuleId(source);
215
- },
216
- // load the PNG icon files, generate final PNG sprites and CSS file
217
- async load(moduleId) {
218
- // only handle the target modules specified in "resolveId"
219
- const target = helper.matchVirtualModuleId(moduleId);
220
- if (!target) {
221
- return;
222
- }
223
- // generate the CSS markup
224
- if (target === cssName) {
225
- return await helper.generateCssMarkupModule();
226
- }
227
- // generate a PNG sprite (export binary data as plain Base64 encoded)
228
- if (target in sprites) {
229
- const { factor, src } = sprites[target];
230
- return await helper.generateSpriteModule(target, factor, src);
231
- }
232
- // invalid module identifier
233
- helper.fail("unknown output file %f", target);
234
- return undefined;
235
- },
236
- };
237
- }
@@ -1,32 +0,0 @@
1
- import type { Plugin } from "vite";
2
- import { type SpritePluginBaseOptions } from "./helper.js";
3
- /**
4
- * Configuration options for generating an SVG sprite from SVG source images.
5
- */
6
- export interface SvgSpritePluginOptions extends SpritePluginBaseOptions<"svg"> {
7
- /**
8
- * Module name of the SVG sprite to be generated (with ".svg" extension).
9
- * The SVG markup of the sprite can be imported from the virtual module
10
- * path `"virtual:svg/[spriteName]"`.
11
- */
12
- spriteName: string;
13
- /**
14
- * A prefix to be added to all icon identifiers declared in the mapping
15
- * file. By default, no prefix will be added.
16
- */
17
- idPrefix?: string;
18
- }
19
- /**
20
- * A plugin for Vite to generate an SVG sprite file from multiple SVG source
21
- * files.
22
- *
23
- * @param index
24
- * URL of the index source file of this plugin.
25
- *
26
- * @param options
27
- * Plugin configuration options.
28
- *
29
- * @returns
30
- * The plugin instance.
31
- */
32
- export declare function svgSpritePlugin(index: string, options: SvgSpritePluginOptions): Plugin;
@@ -1,119 +0,0 @@
1
- import { basename } from "node:path";
2
- import SVGSprite from "svg-sprite";
3
- import { SpritePluginHelper } from "./helper.js";
4
- // class SvgSpritePluginHelper ================================================
5
- class SvgSpritePluginHelper extends SpritePluginHelper {
6
- constructor(index, options) {
7
- super({
8
- pluginIndex: index,
9
- virtualModules: options.spriteName,
10
- }, {
11
- idPrefix: "",
12
- ...options,
13
- });
14
- }
15
- // public methods ---------------------------------------------------------
16
- /**
17
- * Generates the ES source module for an SVG sprite.
18
- *
19
- * @returns
20
- * The source module containing the SVG markup of the sprite.
21
- */
22
- async generateSpriteModule() {
23
- // shortcuts to plugin options
24
- const { imagesPath, idPrefix, spriteName } = this.options;
25
- // try to resolve cached version of generated file
26
- return await this.generateModule(spriteName, async () => {
27
- this.info("compiling sprite %f", spriteName);
28
- // create a reverse map from icon filenames to icon identifiers
29
- const iconIdMap = new Map();
30
- // create the core SVG spriter
31
- const spriter = new SVGSprite({
32
- shape: {
33
- id: {
34
- generator: name => iconIdMap.get(name) ?? "",
35
- },
36
- spacing: {
37
- box: "icon",
38
- },
39
- dimension: {
40
- maxWidth: 16,
41
- maxHeight: 16,
42
- },
43
- },
44
- svg: {
45
- xmlDeclaration: false,
46
- // - remove all <style> elements declaring a class with fill color
47
- // - remove all class attribute referring to the class from <style>
48
- // - add "fill" attribute to all <symbol> elements
49
- transform: markup => markup
50
- .replace(/<style>.*?<\/style>/g, "")
51
- .replace(/class=".*?" ?/g, "")
52
- .replace(/<symbol /g, '<symbol fill="currentColor" '),
53
- },
54
- mode: {
55
- inline: true,
56
- symbol: {
57
- sprite: spriteName,
58
- render: { less: false },
59
- },
60
- },
61
- });
62
- // load all source SVG images
63
- for (const { iconId, iconPath, iconLang } of await this.readIconMapping()) {
64
- const path = `${imagesPath}/${iconPath}.svg`;
65
- // icon identifier with configured prefix and language suffix
66
- const idSuffix = iconLang ? `;${iconLang}` : "";
67
- iconIdMap.set(basename(path), idPrefix + iconId + idSuffix);
68
- // read SVG source image, insert into spriter instance
69
- const markup = await this.readText(path);
70
- spriter.add(path, null, markup);
71
- }
72
- // compile the SVG sprite
73
- const result = (await spriter.compileAsync()).result;
74
- const spriteMarkup = result.symbol.sprite.contents.toString("utf8");
75
- const moduleCode = `export default ${JSON.stringify(spriteMarkup)};`;
76
- this.info("sprite %f generated successfully", spriteName);
77
- return moduleCode;
78
- });
79
- }
80
- }
81
- // plugin =====================================================================
82
- /**
83
- * A plugin for Vite to generate an SVG sprite file from multiple SVG source
84
- * files.
85
- *
86
- * @param index
87
- * URL of the index source file of this plugin.
88
- *
89
- * @param options
90
- * Plugin configuration options.
91
- *
92
- * @returns
93
- * The plugin instance.
94
- */
95
- export function svgSpritePlugin(index, options) {
96
- // helper instance for file system access, logging, etc.
97
- const helper = new SvgSpritePluginHelper(index, options);
98
- // create and return the plugin object
99
- return {
100
- name: "@open-xchange/vite-plugin-icon-sprite/svg",
101
- // initialize file system cache for generated modules
102
- async configResolved(viteConfig) {
103
- await helper.initializeCache(viteConfig);
104
- },
105
- // pick matching imports
106
- resolveId(source) {
107
- return helper.resolveVirtualModuleId(source);
108
- },
109
- // load the SVG icon files, generate final SVG sprite
110
- async load(moduleId) {
111
- // only handle the output file specified in "resolveId"
112
- if (!helper.matchVirtualModuleId(moduleId)) {
113
- return null;
114
- }
115
- // create an ES module that exports the SVG markup of the sprite
116
- return await helper.generateSpriteModule();
117
- },
118
- };
119
- }
@@ -1,7 +0,0 @@
1
- {
2
- "$schema": "https://json.schemastore.org/tsconfig",
3
- "extends": "../../../tsconfig.shared.json",
4
- "compilerOptions": {
5
- "outDir": "../dist"
6
- }
7
- }