Skip to content

Commit 85a7f3c

Browse files
committed
feat: migrate to vite
1 parent 9d4359a commit 85a7f3c

File tree

8 files changed

+3619
-333
lines changed

8 files changed

+3619
-333
lines changed

package-lock.json

Lines changed: 3239 additions & 317 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@
99
"test:watch": "mocha --opts tests/node.opts --watch",
1010
"test:browser": "karma start",
1111
"lint": "standard",
12-
"build": "gulp",
13-
"clean": "gulp clean",
14-
"prepublishOnly": "npm run clean && npm run build"
12+
"prepublishOnly": "npm run clean && npm run build",
13+
"vite:build": "vite build && vite build --config vite.config.node.mts",
14+
"build": "vite build && node build/process-emojis.js && webpack",
15+
"clean": "rimraf dist"
1516
},
1617
"types": "dist/index.d.ts",
1718
"repository": {
@@ -33,14 +34,19 @@
3334
"@babel/plugin-transform-runtime": "^7.26.9",
3435
"@babel/preset-env": "^7.26.9",
3536
"@babel/preset-typescript": "^7.26.0",
37+
"@rollup/plugin-replace": "^6.0.2",
38+
"@rollup/plugin-typescript": "^12.1.2",
3639
"@types/chai": "^5.2.0",
3740
"@types/mocha": "^10.0.10",
3841
"babel-loader": "^10.0.0",
3942
"chai": "^5.2.0",
43+
"clean-css": "^5.3.3",
4044
"del": "^8.0.0",
4145
"emoji-datasource-google": "^15.1.2",
46+
"glob": "^11.0.1",
4247
"gulp": "^5.0.0",
4348
"gulp-bump": "^3.2.0",
49+
"gulp-clean-css": "^4.3.0",
4450
"gulp-clone": "^2.0.1",
4551
"gulp-concat": "^2.6.1",
4652
"gulp-download": "0.0.1",
@@ -49,12 +55,12 @@
4955
"gulp-insert": "^0.5.0",
5056
"gulp-jshint": "^2.1.0",
5157
"gulp-load-plugins": "^2.0.8",
52-
"gulp-clean-css": "^4.3.0",
5358
"gulp-rename": "^2.0.0",
5459
"gulp-replace": "^1.1.4",
5560
"gulp-typescript": "^6.0.0-alpha.1",
5661
"gulp-uglify": "^3.0.2",
5762
"gulp-unzip": "^1.1.0",
63+
"image-data-uri": "^2.0.1",
5864
"inquirer": "^12.4.2",
5965
"jsdom": "^26.0.0",
6066
"jsdom-global": "^3.0.2",
@@ -67,10 +73,17 @@
6773
"minimatch": "^10.0.1",
6874
"mocha": "^11.1.0",
6975
"puppeteer": "^24.4.0",
76+
"rimraf": "^6.0.1",
77+
"rollup-plugin-copy": "^3.5.0",
7078
"standard": "^17.1.2",
7179
"through2": "^4.0.2",
7280
"ts-node": "^10.9.2",
7381
"typescript": "^5.8.2",
82+
"vite": "^6.2.1",
83+
"vite-plugin-banner": "^0.8.0",
84+
"vite-plugin-dts": "^4.5.3",
85+
"webpack": "^5.98.0",
86+
"webpack-cli": "^6.0.1",
7487
"webpack-stream": "^7.0.0"
7588
},
7689
"dependencies": {

src/emoji-datasource-google.d.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
declare module "emoji-datasource-google" {
2+
// types generated by quicktype
3+
4+
export interface SkinVariation {
5+
unified: string;
6+
non_qualified: null | string;
7+
image: string;
8+
sheet_x: number;
9+
sheet_y: number;
10+
added_in: string;
11+
has_img_apple: boolean;
12+
has_img_google: boolean;
13+
has_img_twitter: boolean;
14+
has_img_facebook: boolean;
15+
obsoletes?: string;
16+
obsoleted_by?: string;
17+
}
18+
19+
export type Category =
20+
| "Activities"
21+
| "Animals & Nature"
22+
| "Component"
23+
| "Flags"
24+
| "Food & Drink"
25+
| "Objects"
26+
| "People & Body"
27+
| "Smileys & Emotion"
28+
| "Symbols"
29+
| "Travel & Places";
30+
31+
export interface Emoji {
32+
name: string;
33+
unified: string;
34+
non_qualified: null | string;
35+
docomo: null | string;
36+
au: null | string;
37+
softbank: null | string;
38+
google: null | string;
39+
image: string;
40+
sheet_x: number;
41+
sheet_y: number;
42+
short_name: string;
43+
short_names: string[];
44+
text: null | string;
45+
texts: string[] | null;
46+
category: Category;
47+
subcategory: string;
48+
sort_order: number;
49+
added_in: string;
50+
has_img_apple: boolean;
51+
has_img_google: boolean;
52+
has_img_twitter: boolean;
53+
has_img_facebook: boolean;
54+
skin_variations?: Record<string, SkinVariation>;
55+
obsoletes?: string;
56+
obsoleted_by?: string;
57+
}
58+
59+
declare const emojiList: Emoji[];
60+
export default emojiList;
61+
}

src/index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ interface Window {
55
document: typeof document
66
}
77

8-
const namedEmojiString = /*##EMOJILIST*/'';
8+
const namedEmojiString = '#EMOJILIST#';
99
const namedEmoji = namedEmojiString.split(/,/);
1010
const namedMatchHash = new Set(namedEmoji);
1111

@@ -221,7 +221,7 @@ class Emojify {
221221
});
222222
}
223223

224-
defaultReplacer(emoji: string, name: string): string {
224+
defaultReplacer(_emoji: string, name: string): string {
225225
const elementType =
226226
this.config.tag_type || modeToElementTagType(this.config.mode!);
227227
if (elementType !== 'img') {
@@ -365,7 +365,6 @@ class Emojify {
365365

366366
return win.NodeFilter.FILTER_SKIP;
367367
} as any,
368-
false,
369368
);
370369

371370
var node;

tsconfig.json

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
11
{
22
"compilerOptions": {
3-
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
4-
"strict": true, /* Enable all strict type-checking options. */
5-
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
6-
"lib": [
7-
"dom",
8-
"es2017"
9-
],
3+
"target": "ES2020",
104
"rootDir": "src",
115
"outDir": "dist",
12-
"declaration": true
13-
}
6+
"declaration": true,
7+
8+
"useDefineForClassFields": true,
9+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
10+
"module": "ESNext",
11+
"skipLibCheck": true,
12+
13+
/* Bundler mode */
14+
"moduleResolution": "bundler",
15+
"allowImportingTsExtensions": true,
16+
"isolatedModules": true,
17+
"moduleDetection": "force",
18+
"emitDeclarationOnly": true,
19+
20+
/* Linting */
21+
"strict": true,
22+
"noUnusedLocals": true,
23+
"noUnusedParameters": true,
24+
"noFallthroughCasesInSwitch": true,
25+
"noUncheckedSideEffectImports": true
26+
},
27+
"include": ["src"]
1428
}

vite-plugins.mts

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import path from "path";
2+
import fs from "fs";
3+
import fsExtra from "fs-extra";
4+
import { glob } from "glob";
5+
import CleanCSS from "clean-css";
6+
import imageDataURI from "image-data-uri";
7+
import emojiData from "emoji-datasource-google" with { type: "json" };
8+
9+
/**
10+
* Plugin to clean the entire dist folder before build.
11+
*/
12+
export function cleanPlugin() {
13+
return {
14+
name: "clean",
15+
buildStart() {
16+
const distPath = path.resolve(__dirname, "dist");
17+
fsExtra.removeSync(distPath);
18+
},
19+
};
20+
}
21+
22+
/**
23+
* Plugin to copy and rename emoji images from emoji-datasource-google.
24+
*/
25+
export function copyImagesPlugin() {
26+
return {
27+
name: "copy-images",
28+
async buildStart() {
29+
const imageDest = path.resolve(__dirname, "dist/images/basic");
30+
await fsExtra.remove(imageDest);
31+
await fsExtra.ensureDir(imageDest);
32+
33+
// Build a map where key = original image filename, value = emoji info
34+
const emojiMap = emojiData.reduce((acc, emoji) => {
35+
acc[emoji.image] = emoji;
36+
return acc;
37+
}, {});
38+
const availableFiles = Object.keys(emojiMap);
39+
const srcDir = path.resolve(
40+
__dirname,
41+
"node_modules/emoji-datasource-google/img/google/64",
42+
);
43+
44+
// Copy primary emoji file (renaming to its short_name)
45+
const files = glob.sync(`${srcDir}/*.png`);
46+
for (const file of files) {
47+
const base = path.basename(file);
48+
if (availableFiles.includes(base)) {
49+
const emoji = emojiMap[base];
50+
const destFile = path.resolve(imageDest, `${emoji.short_name}.png`);
51+
await fsExtra.copy(file, destFile);
52+
}
53+
}
54+
// Copy extra aliases (if any)
55+
for (const base of availableFiles) {
56+
const emoji = emojiMap[base];
57+
const aliases = emoji.short_names.filter((n) => n !== emoji.short_name);
58+
const srcFile = path.resolve(srcDir, base);
59+
if (aliases.length > 0 && fs.existsSync(srcFile)) {
60+
for (const alias of aliases) {
61+
const destFile = path.resolve(imageDest, `${alias}.png`);
62+
await fsExtra.copy(srcFile, destFile);
63+
}
64+
}
65+
}
66+
},
67+
};
68+
}
69+
70+
/**
71+
* Plugin to copy CSS files from src/css/basic to dist/css/basic and minify them.
72+
*/
73+
export function copyStylesPlugin() {
74+
return {
75+
name: "copy-styles",
76+
async buildStart() {
77+
const srcStylesDir = path.resolve(__dirname, "src/css/basic");
78+
const destStylesDir = path.resolve(__dirname, "dist/css/basic");
79+
await fsExtra.ensureDir(destStylesDir);
80+
const files = glob.sync(`${srcStylesDir}/*.css`);
81+
for (const file of files) {
82+
const destFile = path.resolve(destStylesDir, path.basename(file));
83+
await fsExtra.copy(file, destFile);
84+
// Minify the CSS
85+
const css = fs.readFileSync(file, "utf8");
86+
const minified = new CleanCSS().minify(css).styles;
87+
const minFile = path.resolve(
88+
destStylesDir,
89+
path.basename(file, ".css") + ".min.css",
90+
);
91+
fs.writeFileSync(minFile, minified, "utf8");
92+
}
93+
},
94+
};
95+
}
96+
97+
/**
98+
* Plugin to generate CSS with data URIs for a set of emoticons.
99+
*/
100+
export function dataURIPlugin() {
101+
return {
102+
name: "generate-data-uri-css",
103+
async closeBundle() {
104+
const imageDir = path.resolve(__dirname, "dist/images/basic");
105+
const cssDataURIPath = path.resolve(__dirname, "dist/css/data-uri");
106+
await fsExtra.ensureDir(cssDataURIPath);
107+
const files = glob.sync(`${imageDir}/*.png`);
108+
// Emoticon list (as in your gulpfile)
109+
const emoticons = [
110+
"smile",
111+
"scream",
112+
"smirk",
113+
"grinning",
114+
"stuck_out_tongue_closed_eyes",
115+
"stuck_out_tongue_winking_eye",
116+
"rage",
117+
"frowning",
118+
"sob",
119+
"kissing_heart",
120+
"wink",
121+
"pensive",
122+
"confounded",
123+
"flushed",
124+
"relaxed",
125+
"mask",
126+
"heart",
127+
"broken_heart",
128+
];
129+
let cssContent = "";
130+
let emoticonsContent = "";
131+
// For each image that is in the emoticon list, generate a CSS rule with its data URI.
132+
for (const file of files) {
133+
const name = path.basename(file, ".png");
134+
const dataUri = await imageDataURI.encodeFromFile(file, "PNG");
135+
const str = `.emoji-${name === "+1" ? "plus1" : name} { background-image: url("${dataUri}"); }\n`;
136+
cssContent += str;
137+
if (emoticons.includes(name)) emoticonsContent += str;
138+
}
139+
// Write unminified CSS file
140+
fs.writeFileSync(
141+
path.resolve(cssDataURIPath, "emojify.css"),
142+
cssContent,
143+
"utf8",
144+
);
145+
// Write minified version
146+
fs.writeFileSync(
147+
path.resolve(cssDataURIPath, "emojify.min.css"),
148+
new CleanCSS().minify(cssContent).styles,
149+
"utf8",
150+
);
151+
152+
const cssFile = path.resolve(cssDataURIPath, "emojify-emoticons.css");
153+
fs.writeFileSync(
154+
path.resolve(cssDataURIPath, "emojify-emoticons.css"),
155+
emoticonsContent,
156+
"utf8",
157+
);
158+
fs.writeFileSync(
159+
path.resolve(cssDataURIPath, "emojify-emoticons.min.css"),
160+
new CleanCSS().minify(emoticonsContent).styles,
161+
"utf8",
162+
);
163+
},
164+
};
165+
}
166+
167+
/**
168+
* Plugin to replace the token "\/*##EMOJILIST*\/\n'';" with a string
169+
* built from the names of the emoji images that were copied.
170+
*/
171+
export function replaceEmojiListPlugin() {
172+
return {
173+
name: "replace-emoji-list",
174+
generateBundle(options, bundle) {
175+
const imageDir = path.resolve(__dirname, "dist/images/basic");
176+
let emojiString = "";
177+
if (fs.existsSync(imageDir)) {
178+
const files = fs.readdirSync(imageDir);
179+
emojiString = files
180+
.filter((f) => f.endsWith(".png"))
181+
.map((f) => path.basename(f, ".png"))
182+
.join(",");
183+
}
184+
// For each JS chunk in the bundle, do the replacement.
185+
for (const fileName in bundle) {
186+
const chunk = bundle[fileName];
187+
if (chunk.type === "chunk") {
188+
chunk.code = chunk.code.replace(/#EMOJILIST#/, `${emojiString}`);
189+
}
190+
}
191+
},
192+
};
193+
}

0 commit comments

Comments
 (0)