-
-
Notifications
You must be signed in to change notification settings - Fork 19
/
esbuild.mjs
189 lines (167 loc) · 7.74 KB
/
esbuild.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
import { build } from 'esbuild';
import { sassPlugin } from 'esbuild-sass-plugin'
import fs from 'fs';
import path from 'path';
import { glob } from 'glob';
const resourcesDir = "resources";
const distDir = "dist";
const distLegacyDir = "dist_legacy";
const extensionBanner = `// For GNOME Shell version before 45
class Extension {
constructor(meta) { // meta has type ExtensionMeta
this.metadata = meta.metadata;
this.uuid = meta.uuid;
this.path = meta.path;
}
getSettings() {
return imports.misc.extensionUtils.getSettings();
}
static openPrefs() {
return imports.misc.extensionUtils.openPrefs();
}
}
class Mtk { Rectangle }
Mtk.Rectangle = function (params = {}) {
return new imports.gi.Meta.Rectangle(params);
};
Mtk.Rectangle.$gtype = imports.gi.Meta.Rectangle.$gtype;
`;
const extensionFooter = `
function init(meta) {
imports.misc.extensionUtils.initTranslations();
return new TilingShellExtension(meta);
}
`;
const prefsBanner = `// For GNOME Shell version before 45
const Config = imports.misc.config;
class ExtensionPreferences {
constructor(metadata) {
this.metadata = metadata;
}
getSettings() {
return imports.misc.extensionUtils.getSettings();
}
}
`;
const prefsFooter = `
function init() {
imports.misc.extensionUtils.initTranslations();
}
function fillPreferencesWindow(window) {
const metadata = imports.misc.extensionUtils.getCurrentExtension().metadata;
const prefs = new TilingShellExtensionPreferences(metadata);
prefs.fillPreferencesWindow(window);
}
`;
/// Converts imports on the form
/// import { a, b, c } from 'gi://Source'
///
/// to
///
/// const Source = imports.gi;
/// and
/// const { a, b, c } = imports.gi.Source;
/// If the imported module is Mtk, it is aliased with Meta
function convertImports(text) {
// drop import of Extension class
text = text.replaceAll('import { Extension } from "resource:///org/gnome/shell/extensions/extension.js";', "");
// drop import of ExtensionPreferences class
text = text.replaceAll('import { ExtensionPreferences } from "resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js";', "");
// drop import of Config from preferences
text = text.replaceAll('import * as Config from "resource:///org/gnome/Shell/Extensions/js/misc/config.js";', "");
// replace import of translation related code
const regexTranslation = new RegExp(`import {(.*|\n.*)?gettext as _[^from]*[^;]*;`, 'gm');
text = text.replaceAll(regexTranslation, "const { gettext: _, ngettext, pgettext } = imports.misc.extensionUtils;");
// replace import of all translation stuff made in translation.ts
//text = text.replaceAll('import { gettext as _, ngettext, pgettext } from "resource:///org/gnome/shell/extensions/extension.js";', "const { gettext: _, ngettext, pgettext } = imports.misc.extensionUtils;");
const regexExportExtension = new RegExp(`export {((.|\n)*)(.+) as default((.|\n)*)};`, 'gm');
text = text.replaceAll(regexExportExtension, "");
// replace import Source from "gi://Source" with const Source = imports.gi.Source;
const regexGi = new RegExp('import (.+) from \\"gi:\\/\\/(.+)\\"', 'gm');
text = text.replaceAll(regexGi, (match, imported, module) => {
if (module.indexOf("Mtk") >= 0) {
// remove first occurrence of Mtk.
// it will be defined by the extension banner
if (imported === "Mtk") return "";
// alias the imported Mtk with the extension banner's Mtk
return `const ${imported} = Mtk`;
}
return `const ${imported} = imports.gi.${module}`;
});
// replace import * as Source from "resource:///org/gnome/shell/path/to/source.js"; with const Source = imports.path.to.Source;
const regexResource = new RegExp('import \\* as (.+) from \\"resource:\\/\\/\\/org\\/gnome\\/shell\\/(.+)\\.js\\"', 'gm');
text = text.replaceAll(regexResource, (match, imported, module) => `const ${imported} = imports.${module.replace('/', '.')}`);
return text;
}
// build extension
build({
logLevel: "info",
entryPoints: ['src/extension.ts', 'src/prefs.ts'],
outdir: distDir,
bundle: true,
treeShaking: false,
// firefox60 // Since GJS 1.53.90
// firefox68 // Since GJS 1.63.90
// firefox78 // Since GJS 1.65.90
// firefox91 // Since GJS 1.71.1
// firefox102 // Since GJS 1.73.2
target: 'firefox78',
platform: 'node',
format: 'esm',
external: ['gi://*', 'resource://*'],
plugins: [sassPlugin()],
}).then(() => {
fs.renameSync(path.resolve(distDir, "extension.css"), path.resolve(distDir, "stylesheet.css"));
fs.cpSync(resourcesDir, distDir, { recursive: true });
// warn if you imported GTK libraries in GNOME Shell (Gdk, Gtk or Adw)
const extensionJSContent = fs.readFileSync(`${distDir}/extension.js`).toString().split('\n');
['Gdk', 'Gtk', 'Adw'].forEach(moduleName => {
for (let lineNumber = 0; lineNumber < extensionJSContent.length; lineNumber++) {
if (extensionJSContent[lineNumber].indexOf(`import ${moduleName}`) >= 0) {
console.error(`⚠️ Error: "${moduleName}" was imported in extension.js at line ${lineNumber}`);
}
}
});
// warn if you imported GNOME Shell libraries in Preferences (Clutter, Meta, St or Shell)
const prefsJSContent = fs.readFileSync(`${distDir}/prefs.js`).toString().split('\n');
['Clutter', 'Meta', 'Mtk', 'St', 'Shell'].forEach(moduleName => {
for (let lineNumber = 0; lineNumber < prefsJSContent.length; lineNumber++) {
if (prefsJSContent[lineNumber].indexOf(`import ${moduleName}`) >= 0) {
console.error(`⚠️ Error: "${moduleName}" was imported in prefs.js at line ${lineNumber}`);
}
}
});
}).then(async () => {
console.log(" 💡", "Generating legacy version...");
// duplicate the build into distLegacyDir
fs.cpSync(distDir, distLegacyDir, { recursive: true });
// for each js file in distLegacyDir, apply conversion
const files = await glob(`${distLegacyDir}/**/*.js`, {});
for (let file of files) {
let jsFileContent = fs.readFileSync(file).toString();
let convertedContent = convertImports(jsFileContent);
if (file.indexOf("extension.js") >= 0) {
fs.writeFileSync(file, `${extensionBanner}${convertedContent}${extensionFooter}`);
} else if (file.indexOf("prefs.js") >= 0) {
fs.writeFileSync(file, `${prefsBanner}${convertedContent}${prefsFooter}`);
} else {
fs.writeFileSync(file, convertedContent);
}
}
const metadataFile = path.resolve(resourcesDir, 'metadata.json');
const metadataJson = JSON.parse(fs.readFileSync(metadataFile));
const legacyShellVersions = metadataJson["shell-version"].filter(version => Number(version) <= 44);
const nonLegacyShellVersions = metadataJson["shell-version"].filter(version => Number(version) > 44);
console.log(" 🚀", "Updating metadata.json file...");
// remove legacy versions from main version's metadata file
metadataJson["shell-version"] = nonLegacyShellVersions;
fs.writeFileSync(path.resolve(distDir, 'metadata.json'), JSON.stringify(metadataJson, null, 4));
// keep legacy versions only from legacy extension's metadata file
metadataJson["shell-version"] = legacyShellVersions;
fs.writeFileSync(path.resolve(distLegacyDir, 'metadata.json'), JSON.stringify(metadataJson, null, 4));
console.log();
console.log("📁 ", "Main version directory: ", distDir);
console.log("📁 ", "Legacy version directory:", distLegacyDir);
console.log("📖 ", "Main version for GNOME Shells: ", nonLegacyShellVersions);
console.log("📖 ", "Legacy version for GNOME Shells:", legacyShellVersions);
});