sliftutils 0.6.4 → 0.6.6
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/builders/extensionBuild.ts +70 -19
- package/builders/webBuild.ts +89 -0
- package/builders/webBuildRun.js +4 -0
- package/package.json +4 -2
- package/spec.txt +22 -8
|
@@ -17,7 +17,7 @@ async function main() {
|
|
|
17
17
|
.option("contentEntry", { type: "string", default: "./extContentScript.ts", desc: `Path to the entry point file` })
|
|
18
18
|
.option("manifestPath", { type: "string", default: "./manifest.json", desc: `Path to the manifest.json file` })
|
|
19
19
|
.option("assetsFolder", { type: "string", default: "./assets", desc: `Path to the assets folder` })
|
|
20
|
-
.option("outputFolder", { type: "string", default: "./build-
|
|
20
|
+
.option("outputFolder", { type: "string", default: "./build-extension", desc: `Output folder` })
|
|
21
21
|
.argv || {}
|
|
22
22
|
;
|
|
23
23
|
|
|
@@ -36,9 +36,6 @@ async function main() {
|
|
|
36
36
|
if (!hasManifest) {
|
|
37
37
|
throw new Error("No manifest file found. Please specify the manifest file with the --manifestPath option. Or, create the default file at ./manifest.json.");
|
|
38
38
|
}
|
|
39
|
-
if (!hasAssets) {
|
|
40
|
-
throw new Error("No assets folder found. Please specify the assets folder with the --assetsFolder option. Or, create the default folder at ./assets.");
|
|
41
|
-
}
|
|
42
39
|
|
|
43
40
|
await fs.promises.mkdir("./build-extension", { recursive: true });
|
|
44
41
|
|
|
@@ -56,24 +53,78 @@ async function main() {
|
|
|
56
53
|
}
|
|
57
54
|
await fs.promises.cp(yargObj.manifestPath, path.join(yargObj.outputFolder, "manifest.json"));
|
|
58
55
|
|
|
59
|
-
|
|
56
|
+
// Parse manifest and collect referenced files
|
|
57
|
+
let manifestContent = await fs.promises.readFile(yargObj.manifestPath, "utf-8");
|
|
58
|
+
let manifest = JSON.parse(manifestContent);
|
|
60
59
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
60
|
+
// Collect all files to copy
|
|
61
|
+
let filesToCopy: string[] = [];
|
|
62
|
+
|
|
63
|
+
// Helper to add icons (can be string or object of strings)
|
|
64
|
+
function addIconPaths(icon: string | object | undefined) {
|
|
65
|
+
if (!icon) return;
|
|
66
|
+
if (typeof icon === "string") {
|
|
67
|
+
filesToCopy.push(icon);
|
|
68
|
+
} else if (typeof icon === "object") {
|
|
69
|
+
for (const iconPath of Object.values(icon)) {
|
|
70
|
+
if (typeof iconPath === "string") {
|
|
71
|
+
filesToCopy.push(iconPath);
|
|
72
|
+
}
|
|
71
73
|
}
|
|
72
74
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Add manifest-referenced files
|
|
78
|
+
if (manifest.action?.default_popup) filesToCopy.push(manifest.action.default_popup);
|
|
79
|
+
if (manifest.browser_action?.default_popup) filesToCopy.push(manifest.browser_action.default_popup);
|
|
80
|
+
if (manifest.page_action?.default_popup) filesToCopy.push(manifest.page_action.default_popup);
|
|
81
|
+
if (manifest.options_page) filesToCopy.push(manifest.options_page);
|
|
82
|
+
if (manifest.options_ui?.page) filesToCopy.push(manifest.options_ui.page);
|
|
83
|
+
if (manifest.devtools_page) filesToCopy.push(manifest.devtools_page);
|
|
84
|
+
if (manifest.sidebar_action?.default_panel) filesToCopy.push(manifest.sidebar_action.default_panel);
|
|
85
|
+
if (manifest.chrome_url_overrides?.newtab) filesToCopy.push(manifest.chrome_url_overrides.newtab);
|
|
86
|
+
if (manifest.chrome_url_overrides?.bookmarks) filesToCopy.push(manifest.chrome_url_overrides.bookmarks);
|
|
87
|
+
if (manifest.chrome_url_overrides?.history) filesToCopy.push(manifest.chrome_url_overrides.history);
|
|
88
|
+
|
|
89
|
+
// Add icons
|
|
90
|
+
addIconPaths(manifest.icons);
|
|
91
|
+
addIconPaths(manifest.action?.default_icon);
|
|
92
|
+
addIconPaths(manifest.browser_action?.default_icon);
|
|
93
|
+
addIconPaths(manifest.page_action?.default_icon);
|
|
94
|
+
|
|
95
|
+
// Add assets folder files if it exists
|
|
96
|
+
if (hasAssets) {
|
|
97
|
+
for await (const file of getAllFiles(yargObj.assetsFolder)) {
|
|
98
|
+
filesToCopy.push(file);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Copy all files with timestamp checking
|
|
103
|
+
async function getTimestamp(filePath: string): Promise<number> {
|
|
104
|
+
try {
|
|
105
|
+
const stats = await fs.promises.stat(filePath);
|
|
106
|
+
return stats.mtimeMs;
|
|
107
|
+
} catch (error) {
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let filesCopied = 0;
|
|
113
|
+
let root = path.resolve(".");
|
|
114
|
+
for (const file of filesToCopy) {
|
|
115
|
+
let sourcePath = path.resolve(file);
|
|
116
|
+
if (!fs.existsSync(sourcePath)) {
|
|
117
|
+
console.warn(`Warning: File not found: ${file}`);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
let relativePath = path.relative(root, sourcePath);
|
|
121
|
+
let destPath = path.join(yargObj.outputFolder, relativePath);
|
|
122
|
+
|
|
123
|
+
let sourceTimestamp = await getTimestamp(sourcePath);
|
|
124
|
+
let destTimestamp = await getTimestamp(destPath);
|
|
125
|
+
if (sourceTimestamp > destTimestamp) {
|
|
126
|
+
await fs.promises.mkdir(path.dirname(destPath), { recursive: true });
|
|
127
|
+
await fs.promises.cp(sourcePath, destPath);
|
|
77
128
|
filesCopied++;
|
|
78
129
|
}
|
|
79
130
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import { delay } from "socket-function/src/batching";
|
|
3
|
+
import { bundleEntryCaller } from "../bundler/bundleEntryCaller";
|
|
4
|
+
import yargs from "yargs";
|
|
5
|
+
import { formatTime } from "socket-function/src/formatting/format";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import { getAllFiles } from "../misc/fs";
|
|
8
|
+
|
|
9
|
+
async function main() {
|
|
10
|
+
let time = Date.now();
|
|
11
|
+
let yargObj = yargs(process.argv)
|
|
12
|
+
.option("entryPoint", { type: "string", default: "./browser.tsx", desc: `Path to the entry point file` })
|
|
13
|
+
.option("indexHtml", { type: "string", default: "./index.html", desc: `Path to the index.html file` })
|
|
14
|
+
.option("assetsFolder", { type: "string", default: "./assets", desc: `Path to the assets folder` })
|
|
15
|
+
.option("outputFolder", { type: "string", default: "./build-web", desc: `Output folder` })
|
|
16
|
+
.argv || {}
|
|
17
|
+
;
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
// Wait for any async functions to load.
|
|
21
|
+
await delay(0);
|
|
22
|
+
|
|
23
|
+
let hasEntryPoint = fs.existsSync(yargObj.entryPoint);
|
|
24
|
+
let hasIndexHtml = fs.existsSync(yargObj.indexHtml);
|
|
25
|
+
let hasAssets = fs.existsSync(yargObj.assetsFolder);
|
|
26
|
+
|
|
27
|
+
if (!hasEntryPoint) {
|
|
28
|
+
throw new Error(`Entry point not found at ${yargObj.entryPoint}. Please specify the entry point with the --entryPoint option.`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
await fs.promises.mkdir(yargObj.outputFolder, { recursive: true });
|
|
32
|
+
|
|
33
|
+
await bundleEntryCaller({
|
|
34
|
+
entryPoint: yargObj.entryPoint,
|
|
35
|
+
outputFolder: yargObj.outputFolder,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Collect all files to copy
|
|
39
|
+
let filesToCopy: string[] = [];
|
|
40
|
+
|
|
41
|
+
if (hasIndexHtml) {
|
|
42
|
+
filesToCopy.push(yargObj.indexHtml);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Add assets folder files if it exists
|
|
46
|
+
if (hasAssets) {
|
|
47
|
+
for await (const file of getAllFiles(yargObj.assetsFolder)) {
|
|
48
|
+
filesToCopy.push(file);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Copy all files with timestamp checking
|
|
53
|
+
async function getTimestamp(filePath: string): Promise<number> {
|
|
54
|
+
try {
|
|
55
|
+
const stats = await fs.promises.stat(filePath);
|
|
56
|
+
return stats.mtimeMs;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let filesCopied = 0;
|
|
63
|
+
let root = path.resolve(".");
|
|
64
|
+
for (const file of filesToCopy) {
|
|
65
|
+
let sourcePath = path.resolve(file);
|
|
66
|
+
if (!fs.existsSync(sourcePath)) {
|
|
67
|
+
console.warn(`Warning: File not found: ${file}`);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
let relativePath = path.relative(root, sourcePath);
|
|
71
|
+
let destPath = path.join(yargObj.outputFolder, relativePath);
|
|
72
|
+
|
|
73
|
+
let sourceTimestamp = await getTimestamp(sourcePath);
|
|
74
|
+
let destTimestamp = await getTimestamp(destPath);
|
|
75
|
+
if (sourceTimestamp > destTimestamp) {
|
|
76
|
+
await fs.promises.mkdir(path.dirname(destPath), { recursive: true });
|
|
77
|
+
await fs.promises.cp(sourcePath, destPath);
|
|
78
|
+
filesCopied++;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (filesCopied > 0) {
|
|
82
|
+
console.log(`Copied ${filesCopied} changed files`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let duration = Date.now() - time;
|
|
86
|
+
console.log(`Web build completed in ${formatTime(duration)}`);
|
|
87
|
+
}
|
|
88
|
+
main().catch(console.error).finally(() => process.exit());
|
|
89
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sliftutils",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.6",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"scripts": {
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
"notes": "mobx, preact, socket-function, typenode SHOULD be peerDependencies. But we want to use yarn (better dependency deduplication), so we can't use peerDependencies (as they aren't installed by default, which makes them a nightmare to use). If you want to override the versions, feel free to use overrides/resolutions."
|
|
10
10
|
},
|
|
11
11
|
"bin": {
|
|
12
|
-
"build-nodejs": "./builders/nodeJSBuildRun.js"
|
|
12
|
+
"build-nodejs": "./builders/nodeJSBuildRun.js",
|
|
13
|
+
"build-extension": "./builders/extensionBuildRun.js",
|
|
14
|
+
"build-web": "./builders/webBuildRun.js"
|
|
13
15
|
},
|
|
14
16
|
"dependencies": {
|
|
15
17
|
"@types/shell-quote": "^1.7.5",
|
package/spec.txt
CHANGED
|
@@ -4,10 +4,6 @@ TODO:
|
|
|
4
4
|
4) New project with:
|
|
5
5
|
|
|
6
6
|
We need bins and entry points for the common types of builds
|
|
7
|
-
extension
|
|
8
|
-
copy manifest.json
|
|
9
|
-
build background.ts, copy it
|
|
10
|
-
build content.ts, copy it
|
|
11
7
|
site
|
|
12
8
|
build site, copy it
|
|
13
9
|
electron
|
|
@@ -15,15 +11,30 @@ TODO:
|
|
|
15
11
|
build render process, copy it
|
|
16
12
|
copy the json config file?
|
|
17
13
|
|
|
18
|
-
build-nodejs
|
|
19
|
-
build-electron
|
|
20
|
-
build-extension
|
|
21
14
|
build-web
|
|
15
|
+
- I guess it's the same as the Node.js build.
|
|
16
|
+
- index.html file, which is optional.
|
|
17
|
+
- Assets folder, which is optional.
|
|
18
|
+
build-electron
|
|
22
19
|
|
|
23
20
|
watch helper script in bin
|
|
24
21
|
- And have it automatically ignore .gitignored files
|
|
25
22
|
- And have it also watch .watchignore
|
|
23
|
+
- accepts port argument to broadcast when the build changes
|
|
26
24
|
|
|
25
|
+
watcher port, and helper function for hot reloading (watchHotReload)
|
|
26
|
+
- accepts port argument
|
|
27
|
+
- if in the browser, refreshes
|
|
28
|
+
- warns if it can't connect to the socket, specifying the watch script that should be used
|
|
29
|
+
- in nodejs, instead use socket-function hot reloading
|
|
30
|
+
- import { onHotReload, watchFilesAndTriggerHotReloading } from "socket-function/hot/HotReloadController";
|
|
31
|
+
- in electron watches and refreshes (no per file reloading)
|
|
32
|
+
- in chrome extension
|
|
33
|
+
- errors if called in content script (must be called in background script)
|
|
34
|
+
- reloads extension
|
|
35
|
+
- option to refresh content script pages in overall watch function
|
|
36
|
+
- On load, if reloaded in the last 10s, tells any pages which loaded content script to refresh their page
|
|
37
|
+
|
|
27
38
|
- HELPER bin to setup the project with:
|
|
28
39
|
(Only if the files don't exist)
|
|
29
40
|
tsconfig.json
|
|
@@ -37,8 +48,11 @@ TODO:
|
|
|
37
48
|
- socket-function
|
|
38
49
|
- typesafecss
|
|
39
50
|
- typedev
|
|
40
|
-
|
|
51
|
+
add the basic entry points for Node.js, the browser, the browser HTML file, and even some files for a Chrome extension, and even for an Electron app.
|
|
52
|
+
- We'll make Electron optional though, and so they'll have to manually install Electron if they want to make an Electron app. We can check for this and warn if they try to build electron without having electron installed.
|
|
41
53
|
Add build and type commands to package.json
|
|
54
|
+
- We have the binaries for the build commands, but adding them to packs.json makes it a lot more discoverable.
|
|
55
|
+
- And it's just very good practice to add the type command to package.json. That way you could just say, what does this command do to the AI? and it'll tell you exactly what it does, instead of nesting
|
|
42
56
|
|
|
43
57
|
EXPOSE our helpers in our main export, in an index.ts file?
|
|
44
58
|
- Will this let us just import them? Hmm...
|