@leftium/gg 0.0.1 â 0.0.2
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/README.md +1 -0
- package/dist/OpenInEditorLink.svelte +28 -0
- package/dist/OpenInEditorLink.svelte.d.ts +5 -0
- package/dist/gg.d.ts +12 -0
- package/dist/gg.js +125 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/open-in-editor.d.ts +2 -0
- package/dist/open-in-editor.js +32 -0
- package/package.json +12 -2
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
- Can be inserted into the middle of expressions (returns the value of the first argument).
|
|
10
10
|
- Can output a link that opens the source file in your editor (like VS Code).
|
|
11
11
|
- Simple to disable (turn all loggs into NOP's for production).
|
|
12
|
+
- Diagnostics/hints in dev console & terminal to help install and configure correctly.
|
|
12
13
|
- Faster to type.
|
|
13
14
|
|
|
14
15
|
## Installation
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { dev } from '$app/environment';
|
|
3
|
+
|
|
4
|
+
let { ggResult } = $props();
|
|
5
|
+
|
|
6
|
+
// svelte-ignore non_reactive_update
|
|
7
|
+
let iframeElement: HTMLIFrameElement;
|
|
8
|
+
|
|
9
|
+
function onclick(event: MouseEvent) {
|
|
10
|
+
iframeElement.src = ggResult.url;
|
|
11
|
+
event.preventDefault();
|
|
12
|
+
}
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
{#if dev}
|
|
16
|
+
[đ<a
|
|
17
|
+
{onclick}
|
|
18
|
+
href={ggResult.url}
|
|
19
|
+
title={`${ggResult.fileName}@${ggResult.functionName}`}
|
|
20
|
+
target="_open-in-editor"
|
|
21
|
+
class="open-in-editor-link"
|
|
22
|
+
>
|
|
23
|
+
{ggResult.fileName}
|
|
24
|
+
</a>
|
|
25
|
+
đ]
|
|
26
|
+
|
|
27
|
+
<iframe bind:this={iframeElement} title="" hidden></iframe>
|
|
28
|
+
{/if}
|
package/dist/gg.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import ErrorStackParser from 'error-stack-parser';
|
|
2
|
+
export declare function gg(): {
|
|
3
|
+
fileName: string;
|
|
4
|
+
functionName: string;
|
|
5
|
+
url: string;
|
|
6
|
+
stack: ErrorStackParser.StackFrame[];
|
|
7
|
+
};
|
|
8
|
+
export declare function gg<T>(arg: T, ...args: unknown[]): T;
|
|
9
|
+
export declare namespace gg {
|
|
10
|
+
var disable: () => string;
|
|
11
|
+
var enable: (namespaces: string) => void;
|
|
12
|
+
}
|
package/dist/gg.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import * as dotenv from 'dotenv';
|
|
2
|
+
import debugFactory from 'debug';
|
|
3
|
+
import ErrorStackParser from 'error-stack-parser';
|
|
4
|
+
import { BROWSER } from 'esm-env';
|
|
5
|
+
import http from 'http';
|
|
6
|
+
function findAvailablePort(startingPort) {
|
|
7
|
+
return new Promise((resolve) => {
|
|
8
|
+
const server = http.createServer();
|
|
9
|
+
server.listen(startingPort, () => {
|
|
10
|
+
const actualPort = server?.address()?.port;
|
|
11
|
+
server.close(() => resolve(actualPort));
|
|
12
|
+
});
|
|
13
|
+
server.on('error', () => {
|
|
14
|
+
// If the port is in use, try the next one
|
|
15
|
+
findAvailablePort(startingPort + 1).then(resolve);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function getServerPort() {
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
if (BROWSER) {
|
|
22
|
+
// Browser environment
|
|
23
|
+
const currentPort = window.location.port || (window.location.protocol === 'https:' ? '443' : '80');
|
|
24
|
+
// Resolve the promise with the detected port
|
|
25
|
+
resolve(currentPort);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
// Node.js environment
|
|
29
|
+
const startingPort = Number(process.env.PORT) || 5173; // Default to Vite's default port
|
|
30
|
+
findAvailablePort(startingPort).then((actualPort) => {
|
|
31
|
+
resolve(actualPort);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
const timestampRegex = /(\?t=\d+)?$/;
|
|
37
|
+
const port = await getServerPort();
|
|
38
|
+
const ggConfig = {
|
|
39
|
+
enabled: true,
|
|
40
|
+
showHints: true,
|
|
41
|
+
openInEditorUrlTemplate: `http://localhost:${port}/__open-in-editor?file=$FILENAME`,
|
|
42
|
+
// The srcRoot contains all source files.
|
|
43
|
+
// filename A : http://localhost:5173/src/routes/+layout.svelte
|
|
44
|
+
// filename B : http://localhost:5173/src/lib/gg.ts
|
|
45
|
+
// srcRootprefix : http://localhost:5173/src/
|
|
46
|
+
// <folderName> group: src
|
|
47
|
+
srcRootPattern: '.*?(/(?<folderName>src|chunks)/)'
|
|
48
|
+
};
|
|
49
|
+
const srcRootRegex = new RegExp(ggConfig.srcRootPattern, 'i');
|
|
50
|
+
// To maintain unique millisecond diffs for each callpoint:
|
|
51
|
+
// - Create a unique log function for each callpoint.
|
|
52
|
+
// - Cache and reuse the same log function for a given callpoint.
|
|
53
|
+
const callpointToLogFunction = new Map();
|
|
54
|
+
function openInEditorUrl(fileName) {
|
|
55
|
+
return ggConfig.openInEditorUrlTemplate.replace('$FILENAME', encodeURIComponent(fileName).replaceAll('%2F', '/'));
|
|
56
|
+
}
|
|
57
|
+
// http://localhost:5173/__open-in-editor?file=src%2Froutes%2F%2Bpage.svelte
|
|
58
|
+
const ggLog = debugFactory('gg');
|
|
59
|
+
// Log some gg info to the JS console/terminal:
|
|
60
|
+
if (ggConfig.showHints) {
|
|
61
|
+
const ggLogTest = ggLog.extend('TEST');
|
|
62
|
+
let ggMessage = '\n';
|
|
63
|
+
// Utilities for forming ggMessage:
|
|
64
|
+
const message = (s) => (ggMessage += `${s}\n`);
|
|
65
|
+
const checkbox = (test) => (test ? 'â
' : 'â');
|
|
66
|
+
const makeHint = (test, ifTrue, ifFalse = '') => (test ? ifTrue : ifFalse);
|
|
67
|
+
console.log(`Loaded gg module. Checking configuration...`);
|
|
68
|
+
if (ggConfig.enabled && ggLogTest.enabled) {
|
|
69
|
+
gg('If you can see this logg, gg configured correctly!');
|
|
70
|
+
message(`No problems detected:`);
|
|
71
|
+
if (BROWSER) {
|
|
72
|
+
message(`âšī¸ If gg output still not visible above, enable "Verbose" log level in browser DevTools.`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
message(`Problems detected; fix all â:`);
|
|
77
|
+
}
|
|
78
|
+
const hint = makeHint(!ggConfig.enabled, ' (Update value in gg.ts file.)');
|
|
79
|
+
message(`${checkbox(ggConfig.enabled)} ggConfig.enabled: ${ggConfig.enabled}${hint}`);
|
|
80
|
+
if (BROWSER) {
|
|
81
|
+
const hint = makeHint(!ggLogTest.enabled, " (Try `localStorage.debug = 'gg:*'`)");
|
|
82
|
+
message(`${checkbox(ggLogTest.enabled)} localStorage.debug: ${localStorage?.debug}${hint}`);
|
|
83
|
+
const { status } = await fetch('/__open-in-editor?file=+');
|
|
84
|
+
message(makeHint(status === 222, `â
(optional) open-in-editor vite plugin detected! (status code: ${status})`, `â ī¸ (optional) open-in-editor vite plugin not detected. (status code: ${status}.) Add plugin in vite.config.ts`));
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const hint = makeHint(!ggLogTest.enabled, ' (Try `DEBUG=gg:* npm dev`)');
|
|
88
|
+
dotenv.config(); // Load the environment variables
|
|
89
|
+
message(`${checkbox(ggLogTest.enabled)} DEBUG env variable: ${process?.env?.DEBUG}${hint}`);
|
|
90
|
+
}
|
|
91
|
+
console.log(ggMessage);
|
|
92
|
+
}
|
|
93
|
+
export function gg(...args) {
|
|
94
|
+
if (!ggConfig.enabled) {
|
|
95
|
+
return args.length ? args[0] : { url: '', stack: [] };
|
|
96
|
+
}
|
|
97
|
+
// Ignore first stack frame, which is always the call to gg() itself.
|
|
98
|
+
const stack = ErrorStackParser.parse(new Error()).splice(1);
|
|
99
|
+
// Example: http://localhost:5173/src/routes/+page.svelte
|
|
100
|
+
const filename = stack[0].fileName?.replace(timestampRegex, '') || '';
|
|
101
|
+
// Example: src/routes/+page.svelte
|
|
102
|
+
const filenameToOpen = filename.replace(srcRootRegex, '$<folderName>/');
|
|
103
|
+
// Example: routes/+page.svelte
|
|
104
|
+
const filenameToDisplay = filename.replace(srcRootRegex, '');
|
|
105
|
+
const { functionName } = stack[0];
|
|
106
|
+
//console.log({ filename, fileNameToOpen: filenameToOpen, fileNameToDisplay: filenameToDisplay });
|
|
107
|
+
// A callpoint is uniquely identified by the filename plus function name
|
|
108
|
+
const callpoint = `${filenameToDisplay}${functionName ? `@${functionName}` : ''}`;
|
|
109
|
+
const ggLogFunction = callpointToLogFunction.get(callpoint) ||
|
|
110
|
+
callpointToLogFunction.set(callpoint, ggLog.extend(callpoint)).get(callpoint);
|
|
111
|
+
if (!args.length) {
|
|
112
|
+
const url = openInEditorUrl(filenameToOpen);
|
|
113
|
+
ggLogFunction(` đđđ ${url} đđđ`);
|
|
114
|
+
return {
|
|
115
|
+
fileName: filenameToDisplay,
|
|
116
|
+
functionName,
|
|
117
|
+
url,
|
|
118
|
+
stack
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
ggLogFunction(...args);
|
|
122
|
+
return args[0];
|
|
123
|
+
}
|
|
124
|
+
gg.disable = debugFactory.disable;
|
|
125
|
+
gg.enable = debugFactory.enable;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Based on: https://github.com/yyx990803/launch-editor/blob/master/packages/launch-editor-middleware/index.js
|
|
2
|
+
import * as url from 'url';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import launch from 'launch-editor';
|
|
5
|
+
export default function openInEditorPlugin(specifiedEditor, srcRoot, onErrorCallback) {
|
|
6
|
+
if (typeof specifiedEditor === 'function') {
|
|
7
|
+
onErrorCallback = specifiedEditor;
|
|
8
|
+
specifiedEditor = undefined;
|
|
9
|
+
}
|
|
10
|
+
if (typeof srcRoot === 'function') {
|
|
11
|
+
onErrorCallback = srcRoot;
|
|
12
|
+
srcRoot = undefined;
|
|
13
|
+
}
|
|
14
|
+
srcRoot = srcRoot || process.cwd();
|
|
15
|
+
return {
|
|
16
|
+
name: 'open-in-editor',
|
|
17
|
+
configureServer(server) {
|
|
18
|
+
server.middlewares.use('/__open-in-editor', (req, res) => {
|
|
19
|
+
const { file } = url.parse(req.url || '', true).query || {};
|
|
20
|
+
if (!file) {
|
|
21
|
+
res.statusCode = 500;
|
|
22
|
+
res.end(`open-in-editor-plugin: required query param "file" is missing.`);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
res.statusCode = 222;
|
|
26
|
+
launch(path.resolve(srcRoot, file), specifiedEditor, onErrorCallback);
|
|
27
|
+
res.end('<p>You may safely close this window.</p><script>window.close()</script>');
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leftium/gg",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/Leftium/gg.git"
|
|
@@ -39,14 +39,23 @@
|
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@eslint/compat": "^1.2.5",
|
|
41
41
|
"@eslint/js": "^9.18.0",
|
|
42
|
+
"@picocss/pico": "^2.1.1",
|
|
42
43
|
"@sveltejs/adapter-vercel": "^5.5.2",
|
|
43
44
|
"@sveltejs/kit": "^2.16.0",
|
|
44
45
|
"@sveltejs/package": "^2.0.0",
|
|
45
46
|
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
|
47
|
+
"@types/debug": "^4.1.12",
|
|
48
|
+
"@types/node": "^22.13.17",
|
|
49
|
+
"add": "^2.0.6",
|
|
50
|
+
"debug": "^4.4.0",
|
|
51
|
+
"dotenv": "^16.4.7",
|
|
52
|
+
"error-stack-parser": "^2.1.4",
|
|
46
53
|
"eslint": "^9.18.0",
|
|
47
54
|
"eslint-config-prettier": "^10.0.1",
|
|
48
55
|
"eslint-plugin-svelte": "^3.0.0",
|
|
56
|
+
"esm-env": "^1.2.2",
|
|
49
57
|
"globals": "^16.0.0",
|
|
58
|
+
"launch-editor": "^2.10.0",
|
|
50
59
|
"prettier": "^3.4.2",
|
|
51
60
|
"prettier-plugin-svelte": "^3.3.3",
|
|
52
61
|
"publint": "^0.3.2",
|
|
@@ -54,7 +63,8 @@
|
|
|
54
63
|
"svelte-check": "^4.0.0",
|
|
55
64
|
"typescript": "^5.0.0",
|
|
56
65
|
"typescript-eslint": "^8.20.0",
|
|
57
|
-
"vite": "^6.0.0"
|
|
66
|
+
"vite": "^6.0.0",
|
|
67
|
+
"vite-plugin-devtools-json": "^0.2.0"
|
|
58
68
|
},
|
|
59
69
|
"keywords": [
|
|
60
70
|
"svelte"
|