@leftium/gg 0.0.1 → 0.0.3

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 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}
@@ -0,0 +1,5 @@
1
+ declare const OpenInEditorLink: import("svelte").Component<{
2
+ ggResult: any;
3
+ }, {}, "">;
4
+ type OpenInEditorLink = ReturnType<typeof OpenInEditorLink>;
5
+ export default OpenInEditorLink;
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,129 @@
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
+ editorLink: false,
42
+ openInEditorUrlTemplate: `http://localhost:${port}/__open-in-editor?file=$FILENAME`,
43
+ // The srcRoot contains all source files.
44
+ // filename A : http://localhost:5173/src/routes/+layout.svelte
45
+ // filename B : http://localhost:5173/src/lib/gg.ts
46
+ // srcRootprefix : http://localhost:5173/src/
47
+ // <folderName> group: src
48
+ srcRootPattern: '.*?(/(?<folderName>src|chunks)/)'
49
+ };
50
+ const srcRootRegex = new RegExp(ggConfig.srcRootPattern, 'i');
51
+ // To maintain unique millisecond diffs for each callpoint:
52
+ // - Create a unique log function for each callpoint.
53
+ // - Cache and reuse the same log function for a given callpoint.
54
+ const namespaceToLogFunction = new Map();
55
+ let maxCallpointLength = 0;
56
+ function openInEditorUrl(fileName) {
57
+ return ggConfig.openInEditorUrlTemplate.replace('$FILENAME', encodeURIComponent(fileName).replaceAll('%2F', '/'));
58
+ }
59
+ // http://localhost:5173/__open-in-editor?file=src%2Froutes%2F%2Bpage.svelte
60
+ const ggLog = debugFactory('gg');
61
+ // Log some gg info to the JS console/terminal:
62
+ if (ggConfig.showHints) {
63
+ const ggLogTest = ggLog.extend('TEST');
64
+ let ggMessage = '\n';
65
+ // Utilities for forming ggMessage:
66
+ const message = (s) => (ggMessage += `${s}\n`);
67
+ const checkbox = (test) => (test ? '✅' : '❌');
68
+ const makeHint = (test, ifTrue, ifFalse = '') => (test ? ifTrue : ifFalse);
69
+ console.log(`Loaded gg module. Checking configuration...`);
70
+ if (ggConfig.enabled && ggLogTest.enabled) {
71
+ gg('If you can see this logg, gg configured correctly!');
72
+ message(`No problems detected:`);
73
+ if (BROWSER) {
74
+ message(`ℹ️ If gg output still not visible above, enable "Verbose" log level in browser DevTools.`);
75
+ }
76
+ }
77
+ else {
78
+ message(`Problems detected; fix all ❌:`);
79
+ }
80
+ const hint = makeHint(!ggConfig.enabled, ' (Update value in gg.ts file.)');
81
+ message(`${checkbox(ggConfig.enabled)} ggConfig.enabled: ${ggConfig.enabled}${hint}`);
82
+ if (BROWSER) {
83
+ const hint = makeHint(!ggLogTest.enabled, " (Try `localStorage.debug = 'gg:*'`)");
84
+ message(`${checkbox(ggLogTest.enabled)} localStorage.debug: ${localStorage?.debug}${hint}`);
85
+ const { status } = await fetch('/__open-in-editor?file=+');
86
+ 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`));
87
+ }
88
+ else {
89
+ const hint = makeHint(!ggLogTest.enabled, ' (Try `DEBUG=gg:* npm dev`)');
90
+ dotenv.config(); // Load the environment variables
91
+ message(`${checkbox(ggLogTest.enabled)} DEBUG env variable: ${process?.env?.DEBUG}${hint}`);
92
+ }
93
+ console.log(ggMessage);
94
+ }
95
+ export function gg(...args) {
96
+ if (!ggConfig.enabled) {
97
+ return args.length ? args[0] : { url: '', stack: [] };
98
+ }
99
+ // Ignore first stack frame, which is always the call to gg() itself.
100
+ const stack = ErrorStackParser.parse(new Error()).splice(1);
101
+ // Example: http://localhost:5173/src/routes/+page.svelte
102
+ const filename = stack[0].fileName?.replace(timestampRegex, '') || '';
103
+ // Example: src/routes/+page.svelte
104
+ const filenameToOpen = filename.replace(srcRootRegex, '$<folderName>/');
105
+ const url = openInEditorUrl(filenameToOpen);
106
+ // Example: routes/+page.svelte
107
+ const filenameToDisplay = filename.replace(srcRootRegex, '');
108
+ const { functionName } = stack[0];
109
+ //console.log({ filename, fileNameToOpen: filenameToOpen, fileNameToDisplay: filenameToDisplay });
110
+ // A callpoint is uniquely identified by the filename plus function name
111
+ const callpoint = `${filenameToDisplay}${functionName ? `@${functionName}` : ''}`;
112
+ maxCallpointLength = Math.max(maxCallpointLength, callpoint.length);
113
+ const namespace = `${callpoint.padEnd(maxCallpointLength, ' ')}\t${ggConfig.editorLink ? url : ''}\n`;
114
+ const ggLogFunction = namespaceToLogFunction.get(namespace) ||
115
+ namespaceToLogFunction.set(namespace, ggLog.extend(namespace)).get(namespace);
116
+ if (!args.length) {
117
+ ggLogFunction(` 📝📝 ${url} 👀👀`);
118
+ return {
119
+ fileName: filenameToDisplay,
120
+ functionName,
121
+ url,
122
+ stack
123
+ };
124
+ }
125
+ ggLogFunction(...['', '\t', ...args]);
126
+ return args[0];
127
+ }
128
+ gg.disable = debugFactory.disable;
129
+ gg.enable = debugFactory.enable;
package/dist/index.d.ts CHANGED
@@ -1 +1,3 @@
1
- export {};
1
+ import { gg } from './gg.js';
2
+ import openInEditorPlugin from './open-in-editor.js';
3
+ export { gg, openInEditorPlugin };
package/dist/index.js CHANGED
@@ -1,2 +1,4 @@
1
- "use strict";
2
1
  // Reexport your entry components here
2
+ import { gg } from './gg.js';
3
+ import openInEditorPlugin from './open-in-editor.js';
4
+ export { gg, openInEditorPlugin };
@@ -0,0 +1,2 @@
1
+ import type { Plugin } from 'vite';
2
+ export default function openInEditorPlugin(specifiedEditor?: undefined, srcRoot?: string | undefined, onErrorCallback?: ((fileName: string, errorMessage: string | null) => void) | undefined): Plugin;
@@ -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.1",
3
+ "version": "0.0.3",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Leftium/gg.git"
@@ -39,14 +39,21 @@
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",
46
51
  "eslint": "^9.18.0",
47
52
  "eslint-config-prettier": "^10.0.1",
48
53
  "eslint-plugin-svelte": "^3.0.0",
54
+ "esm-env": "^1.2.2",
49
55
  "globals": "^16.0.0",
56
+ "launch-editor": "^2.10.0",
50
57
  "prettier": "^3.4.2",
51
58
  "prettier-plugin-svelte": "^3.3.3",
52
59
  "publint": "^0.3.2",
@@ -54,7 +61,8 @@
54
61
  "svelte-check": "^4.0.0",
55
62
  "typescript": "^5.0.0",
56
63
  "typescript-eslint": "^8.20.0",
57
- "vite": "^6.0.0"
64
+ "vite": "^6.0.0",
65
+ "vite-plugin-devtools-json": "^0.2.0"
58
66
  },
59
67
  "keywords": [
60
68
  "svelte"
@@ -63,5 +71,9 @@
63
71
  "onlyBuiltDependencies": [
64
72
  "esbuild"
65
73
  ]
74
+ },
75
+ "dependencies": {
76
+ "dotenv": "^17.2.0",
77
+ "error-stack-parser": "^2.1.4"
66
78
  }
67
79
  }