@gringow/gringow-nextjs 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 ADDED
@@ -0,0 +1,95 @@
1
+ # @gringow/gringow-nextjs
2
+
3
+ ⚠️ Experimental Next.js integration for Gringow. The current implementation is intentionally minimal and primarily
4
+ targets Turbopack builds by running a cache build once at request time.
5
+
6
+ ## What it actually does today
7
+ - Scans source files for `g\`...\`` template literals using the glob pattern from `gringow.config.*` (via
8
+ `@gringow/gringow` config loading).
9
+ - On Turbopack, hooks into `headers()` and triggers a single cache build on first call.
10
+ - The Webpack path currently returns the provided Next config unchanged (no hooks wired yet).
11
+ - No CLI flags (`--gringow=build|clear|reset`) are implemented.
12
+
13
+ If you need a production-ready workflow today, prefer the Vite plugin or the CLI cache builder.
14
+
15
+ ## Installation
16
+ ```bash
17
+ pnpm add @gringow/gringow-nextjs
18
+ # npm install @gringow/gringow-nextjs
19
+ # yarn add @gringow/gringow-nextjs
20
+ ```
21
+
22
+ ## Usage (Turbopack-first)
23
+ ```ts
24
+ // next.config.ts
25
+ import type { NextConfig } from 'next'
26
+ import GringowNextjsPlugin from '@gringow/gringow-nextjs'
27
+
28
+ const withGringow = GringowNextjsPlugin({ debug: process.env.NODE_ENV === 'development' })
29
+
30
+ const nextConfig: NextConfig = {
31
+ // your config
32
+ }
33
+
34
+ export default withGringow(nextConfig)
35
+ ```
36
+
37
+ Run your app normally (`pnpm dev`). The first time the `headers` function resolves (Turbopack), the plugin will:
38
+
39
+ 1) read `gringow.config.*` for `globby` and LLM settings,
40
+ 2) globby-scan files, collecting `g\`...\`` occurrences,
41
+ 3) call the core Gringow runtime to populate `gringow/gringow.json`.
42
+
43
+ ## Options
44
+ ```ts
45
+ type GringowNextjsPluginOptions = {
46
+ debug?: boolean // prints when cache building starts/completes
47
+ }
48
+ ```
49
+
50
+ ## Status & limitations
51
+ - Turbopack only, and even there it runs during `headers()` rather than build hooks.
52
+ - Webpack integration is a stub (no cache build hooks yet).
53
+ - Cache pruning and `--gringow` flags are not implemented.
54
+
55
+ Contributions to complete the Webpack path or to add build-time hooks are welcome.
56
+
57
+ ## License
58
+ MIT
59
+
60
+ export default function HomePage() {
61
+ const name = 'John'
62
+
63
+ return (
64
+ <div>
65
+ <h1>{Gringow`Welcome to our website, ${name}!`}</h1>
66
+ <p>{Gringow`This text will be automatically translated.`}</p>
67
+ </div>
68
+ )
69
+ }
70
+ ```
71
+
72
+ ### 5. Build your cache
73
+ ```bash
74
+ # Pre-populate the translation cache
75
+ npm run build -- --gringow=build
76
+
77
+ # Or clear and rebuild
78
+ npm run build -- --gringow=reset
79
+ ```
80
+
81
+ The translations will be stored in `./gringow/gringow.json` and automatically used in your application.
82
+
83
+ ## How it works
84
+ - `transform`: checks each loaded module, finds `g\`...\`` usage, and seeds the translation cache via `@gringow/gringow`.
85
+ - `buildEnd`: prunes cache entries that no longer exist in source and backfills any new strings discovered by static scanning.
86
+ - `buildStart`: watches for the `--gringow=` flag to run cache maintenance chores outside of normal Vite flows.
87
+
88
+ ## Development scripts
89
+ ```bash
90
+ pnpm run build # Emit dist/ with tsup
91
+ pnpm run watch # Continuous rebuild for local development
92
+ ```
93
+
94
+ ## License
95
+ MIT © Renato Gaspar
@@ -0,0 +1,3 @@
1
+ declare function buildGringowCache(): Promise<void>;
2
+
3
+ export { buildGringowCache };
@@ -0,0 +1,38 @@
1
+ import Gringow, { awaitedConfig } from '@gringow/gringow';
2
+ import { globby } from 'globby';
3
+ import fs from 'fs/promises';
4
+
5
+ // src/build-gringow-cache.ts
6
+
7
+ // src/get-matches-in-code.ts
8
+ function getMatchesInCode(code) {
9
+ const regex = /g`((?:\\`|[^`])+)`/g;
10
+ return [...new Set([...code.matchAll(regex)].map(([, value = ""]) => value))];
11
+ }
12
+
13
+ // src/get-matches-from-file.ts
14
+ async function getMatchesFromFile(filePath) {
15
+ const code = await fs.readFile(filePath, "utf-8");
16
+ return getMatchesInCode(code);
17
+ }
18
+
19
+ // src/plugin.ts
20
+ var localCache = /* @__PURE__ */ new Set();
21
+
22
+ // src/build-gringow-cache.ts
23
+ async function buildGringowCache() {
24
+ const gringowConfig = await awaitedConfig;
25
+ const files = await globby(String(gringowConfig.globby) ?? "./src/**/*.{js,jsx,ts,tsx}");
26
+ for await (const file of files) {
27
+ const matches = await getMatchesFromFile(file);
28
+ for await (const match of matches) {
29
+ if (!String(match).trim()) continue;
30
+ const { cacheId } = await Gringow(String(match).trim());
31
+ if (!localCache.has(cacheId)) {
32
+ localCache.add(cacheId);
33
+ }
34
+ }
35
+ }
36
+ }
37
+
38
+ export { buildGringowCache };
@@ -0,0 +1,3 @@
1
+ declare function getMatchesFromFile(filePath: string): Promise<string[]>;
2
+
3
+ export { getMatchesFromFile };
@@ -0,0 +1,17 @@
1
+ import fs from 'fs/promises';
2
+
3
+ // src/get-matches-from-file.ts
4
+
5
+ // src/get-matches-in-code.ts
6
+ function getMatchesInCode(code) {
7
+ const regex = /g`((?:\\`|[^`])+)`/g;
8
+ return [...new Set([...code.matchAll(regex)].map(([, value = ""]) => value))];
9
+ }
10
+
11
+ // src/get-matches-from-file.ts
12
+ async function getMatchesFromFile(filePath) {
13
+ const code = await fs.readFile(filePath, "utf-8");
14
+ return getMatchesInCode(code);
15
+ }
16
+
17
+ export { getMatchesFromFile };
@@ -0,0 +1,3 @@
1
+ declare function getMatchesInCode(code: string): string[];
2
+
3
+ export { getMatchesInCode };
@@ -0,0 +1,7 @@
1
+ // src/get-matches-in-code.ts
2
+ function getMatchesInCode(code) {
3
+ const regex = /g`((?:\\`|[^`])+)`/g;
4
+ return [...new Set([...code.matchAll(regex)].map(([, value = ""]) => value))];
5
+ }
6
+
7
+ export { getMatchesInCode };
@@ -0,0 +1,3 @@
1
+ declare function getSourceIds(): Promise<Map<string, string>>;
2
+
3
+ export { getSourceIds };
@@ -0,0 +1,35 @@
1
+ import { awaitedConfig, createCacheId } from '@gringow/gringow';
2
+ import { globby } from 'globby';
3
+ import fs from 'fs/promises';
4
+
5
+ // src/get-source-ids.ts
6
+
7
+ // src/get-matches-in-code.ts
8
+ function getMatchesInCode(code) {
9
+ const regex = /g`((?:\\`|[^`])+)`/g;
10
+ return [...new Set([...code.matchAll(regex)].map(([, value = ""]) => value))];
11
+ }
12
+
13
+ // src/get-matches-from-file.ts
14
+ async function getMatchesFromFile(filePath) {
15
+ const code = await fs.readFile(filePath, "utf-8");
16
+ return getMatchesInCode(code);
17
+ }
18
+
19
+ // src/get-source-ids.ts
20
+ async function getSourceIds() {
21
+ const gringowConfig = await awaitedConfig;
22
+ const files = await globby(String(gringowConfig.globby) ?? "./src/**/*.{js,jsx,ts,tsx}");
23
+ const sourceIds = /* @__PURE__ */ new Map();
24
+ for await (const file of files) {
25
+ const matches = await getMatchesFromFile(file);
26
+ for await (const match of matches) {
27
+ if (!String(match).trim()) continue;
28
+ const cacheId = createCacheId(String(match).trim());
29
+ sourceIds.set(cacheId, String(match).trim());
30
+ }
31
+ }
32
+ return sourceIds;
33
+ }
34
+
35
+ export { getSourceIds };
@@ -0,0 +1,3 @@
1
+ export { GringowNextjsPlugin, GringowNextjsPluginOptions, localCache } from './plugin.mjs';
2
+ import '@gringow/gringow';
3
+ import 'next';
package/dist/index.mjs ADDED
@@ -0,0 +1,105 @@
1
+ import Gringow, { awaitedConfig } from '@gringow/gringow';
2
+ import { globby } from 'globby';
3
+ import fs from 'fs/promises';
4
+
5
+ // src/build-gringow-cache.ts
6
+
7
+ // src/get-matches-in-code.ts
8
+ function getMatchesInCode(code) {
9
+ const regex = /g`((?:\\`|[^`])+)`/g;
10
+ return [...new Set([...code.matchAll(regex)].map(([, value = ""]) => value))];
11
+ }
12
+
13
+ // src/get-matches-from-file.ts
14
+ async function getMatchesFromFile(filePath) {
15
+ const code = await fs.readFile(filePath, "utf-8");
16
+ return getMatchesInCode(code);
17
+ }
18
+
19
+ // src/build-gringow-cache.ts
20
+ async function buildGringowCache() {
21
+ const gringowConfig = await awaitedConfig;
22
+ const files = await globby(String(gringowConfig.globby) ?? "./src/**/*.{js,jsx,ts,tsx}");
23
+ for await (const file of files) {
24
+ const matches = await getMatchesFromFile(file);
25
+ for await (const match of matches) {
26
+ if (!String(match).trim()) continue;
27
+ const { cacheId } = await Gringow(String(match).trim());
28
+ if (!localCache.has(cacheId)) {
29
+ localCache.add(cacheId);
30
+ }
31
+ }
32
+ }
33
+ }
34
+
35
+ // src/plugin.ts
36
+ var localCache = /* @__PURE__ */ new Set();
37
+ async function buildCache(options) {
38
+ if (options.debug) {
39
+ console.log(`[Gringow] Scanning source files...`);
40
+ }
41
+ try {
42
+ await buildGringowCache();
43
+ if (options.debug) {
44
+ console.log("[Gringow] Cache build completed");
45
+ }
46
+ } catch (error) {
47
+ console.error("[Gringow] Error building cache:", error);
48
+ throw error;
49
+ }
50
+ }
51
+ function turbopackConfig(options, nextConfig) {
52
+ if (options.debug) {
53
+ console.log(`[Gringow] Detected bundler: Turbopack}`);
54
+ }
55
+ console.log("localCache.size", localCache.size);
56
+ return {
57
+ ...nextConfig,
58
+ // For Turbopack, use the headers hook to run cache build
59
+ // This is called during the build process
60
+ async headers() {
61
+ if (localCache.size === 0) {
62
+ await buildCache(options);
63
+ }
64
+ console.log("nextConfig.headers", await nextConfig.headers);
65
+ return nextConfig.headers ? await nextConfig.headers() : [];
66
+ }
67
+ };
68
+ }
69
+ function webpackConfig(options, nextConfig) {
70
+ if (options.debug) {
71
+ console.log(`[Gringow] Detected bundler: Webpack`);
72
+ }
73
+ return {
74
+ ...nextConfig
75
+ // Webpack configuration (Next.js 15 and earlier, or when explicitly using --webpack)
76
+ // webpack(config: WebpackConfig, context: any) {
77
+ // const userWebpack = nextConfig.webpack
78
+ // if (userWebpack) {
79
+ // config = userWebpack(config, context)
80
+ // }
81
+ // // Skip if using Turbopack
82
+ // if (isTurbopack) {
83
+ // return config
84
+ // }
85
+ // // Only run on client-side production build
86
+ // if (!context.isServer && !context.dev) {
87
+ // config.plugins = config.plugins || []
88
+ // config.plugins.push({
89
+ // apply(compiler: any) {
90
+ // compiler.hooks.beforeCompile.tapPromise('GringowNextjsPlugin', async () => {
91
+ // await buildCache('webpack')
92
+ // })
93
+ // },
94
+ // })
95
+ // }
96
+ // return config
97
+ // },
98
+ };
99
+ }
100
+ function GringowNextjsPlugin(options = {}) {
101
+ const isTurbopack = process.env.TURBOPACK !== "0" && !process.env.WEBPACK;
102
+ return isTurbopack ? turbopackConfig.bind(null, options) : webpackConfig.bind(null, options);
103
+ }
104
+
105
+ export { GringowNextjsPlugin, localCache };
@@ -0,0 +1,3 @@
1
+ declare function isSourceCode(filePath: string): boolean;
2
+
3
+ export { isSourceCode };
@@ -0,0 +1,6 @@
1
+ // src/is-source-code.ts
2
+ function isSourceCode(filePath) {
3
+ return /\.(js|jsx|ts|tsx)$/.test(filePath) && !filePath.includes("node_modules");
4
+ }
5
+
6
+ export { isSourceCode };
@@ -0,0 +1,25 @@
1
+ import { GringowConfig } from '@gringow/gringow';
2
+ import { NextConfig } from 'next';
3
+
4
+ type GringowNextjsPluginOptions = {
5
+ /**
6
+ * Enable debug logging
7
+ * @default false
8
+ */
9
+ debug?: boolean;
10
+ /**
11
+ * Gringow configuration override
12
+ * If not provided, will use cosmiconfig to find config
13
+ */
14
+ config?: Partial<GringowConfig>;
15
+ };
16
+ type GringowBundlerBind = (nextConfig: NextConfig) => NextConfig;
17
+ declare const localCache: Set<string>;
18
+ /**
19
+ * Gringow plugin for Next.js 15+
20
+ * Automatically detects and supports both Webpack and Turbopack
21
+ * Note: Turbopack became stable in Next.js 15 and is the default bundler in Next.js 16
22
+ */
23
+ declare function GringowNextjsPlugin(options?: GringowNextjsPluginOptions): GringowBundlerBind;
24
+
25
+ export { GringowNextjsPlugin, type GringowNextjsPluginOptions, GringowNextjsPlugin as default, localCache };
@@ -0,0 +1,105 @@
1
+ import Gringow, { awaitedConfig } from '@gringow/gringow';
2
+ import { globby } from 'globby';
3
+ import fs from 'fs/promises';
4
+
5
+ // src/build-gringow-cache.ts
6
+
7
+ // src/get-matches-in-code.ts
8
+ function getMatchesInCode(code) {
9
+ const regex = /g`((?:\\`|[^`])+)`/g;
10
+ return [...new Set([...code.matchAll(regex)].map(([, value = ""]) => value))];
11
+ }
12
+
13
+ // src/get-matches-from-file.ts
14
+ async function getMatchesFromFile(filePath) {
15
+ const code = await fs.readFile(filePath, "utf-8");
16
+ return getMatchesInCode(code);
17
+ }
18
+
19
+ // src/build-gringow-cache.ts
20
+ async function buildGringowCache() {
21
+ const gringowConfig = await awaitedConfig;
22
+ const files = await globby(String(gringowConfig.globby) ?? "./src/**/*.{js,jsx,ts,tsx}");
23
+ for await (const file of files) {
24
+ const matches = await getMatchesFromFile(file);
25
+ for await (const match of matches) {
26
+ if (!String(match).trim()) continue;
27
+ const { cacheId } = await Gringow(String(match).trim());
28
+ if (!localCache.has(cacheId)) {
29
+ localCache.add(cacheId);
30
+ }
31
+ }
32
+ }
33
+ }
34
+
35
+ // src/plugin.ts
36
+ var localCache = /* @__PURE__ */ new Set();
37
+ async function buildCache(options) {
38
+ if (options.debug) {
39
+ console.log(`[Gringow] Scanning source files...`);
40
+ }
41
+ try {
42
+ await buildGringowCache();
43
+ if (options.debug) {
44
+ console.log("[Gringow] Cache build completed");
45
+ }
46
+ } catch (error) {
47
+ console.error("[Gringow] Error building cache:", error);
48
+ throw error;
49
+ }
50
+ }
51
+ function turbopackConfig(options, nextConfig) {
52
+ if (options.debug) {
53
+ console.log(`[Gringow] Detected bundler: Turbopack}`);
54
+ }
55
+ console.log("localCache.size", localCache.size);
56
+ return {
57
+ ...nextConfig,
58
+ // For Turbopack, use the headers hook to run cache build
59
+ // This is called during the build process
60
+ async headers() {
61
+ if (localCache.size === 0) {
62
+ await buildCache(options);
63
+ }
64
+ console.log("nextConfig.headers", await nextConfig.headers);
65
+ return nextConfig.headers ? await nextConfig.headers() : [];
66
+ }
67
+ };
68
+ }
69
+ function webpackConfig(options, nextConfig) {
70
+ if (options.debug) {
71
+ console.log(`[Gringow] Detected bundler: Webpack`);
72
+ }
73
+ return {
74
+ ...nextConfig
75
+ // Webpack configuration (Next.js 15 and earlier, or when explicitly using --webpack)
76
+ // webpack(config: WebpackConfig, context: any) {
77
+ // const userWebpack = nextConfig.webpack
78
+ // if (userWebpack) {
79
+ // config = userWebpack(config, context)
80
+ // }
81
+ // // Skip if using Turbopack
82
+ // if (isTurbopack) {
83
+ // return config
84
+ // }
85
+ // // Only run on client-side production build
86
+ // if (!context.isServer && !context.dev) {
87
+ // config.plugins = config.plugins || []
88
+ // config.plugins.push({
89
+ // apply(compiler: any) {
90
+ // compiler.hooks.beforeCompile.tapPromise('GringowNextjsPlugin', async () => {
91
+ // await buildCache('webpack')
92
+ // })
93
+ // },
94
+ // })
95
+ // }
96
+ // return config
97
+ // },
98
+ };
99
+ }
100
+ function GringowNextjsPlugin(options = {}) {
101
+ const isTurbopack = process.env.TURBOPACK !== "0" && !process.env.WEBPACK;
102
+ return isTurbopack ? turbopackConfig.bind(null, options) : webpackConfig.bind(null, options);
103
+ }
104
+
105
+ export { GringowNextjsPlugin, GringowNextjsPlugin as default, localCache };
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@gringow/gringow-nextjs",
3
+ "version": "0.0.2",
4
+ "description": "A Next.js plugin for Gringow AI-powered translation tool",
5
+ "main": "dist/index.mjs",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.mts",
8
+ "files": [
9
+ "dist",
10
+ "README.md"
11
+ ],
12
+ "exports": {
13
+ "./plugin": {
14
+ "types": "./dist/plugin.d.mts",
15
+ "import": "./dist/plugin.mjs",
16
+ "require": "./dist/plugin.mjs"
17
+ }
18
+ },
19
+ "keywords": [
20
+ "gringow",
21
+ "nextjs",
22
+ "next",
23
+ "translation",
24
+ "i18n",
25
+ "internationalization",
26
+ "llm",
27
+ "ai",
28
+ "nextjs-plugin",
29
+ "react"
30
+ ],
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/rntgspr/gringow.git",
34
+ "directory": "gringow-nextjs"
35
+ },
36
+ "author": "Renato Gaspar <rntgspr@gmail.com>",
37
+ "license": "MIT",
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "dependencies": {
42
+ "globby": "14.1.0",
43
+ "react": "^19.2.0",
44
+ "webpack": "^5.102.1",
45
+ "@gringow/gringow": "0.1.3",
46
+ "@gringow/gringow-react": "0.0.7"
47
+ },
48
+ "peerDependencies": {
49
+ "next": ">=15.0.0"
50
+ },
51
+ "devDependencies": {
52
+ "@types/node": "22.15.0",
53
+ "@types/react": "^19.2.7",
54
+ "tsc-alias": "1.8.16",
55
+ "tsup": "8.5.0",
56
+ "typescript": "5.8.3"
57
+ },
58
+ "scripts": {
59
+ "build": "rm -rf ./dist/* ./tsconfig.tsbuildinfo && tsup",
60
+ "watch": "pnpm run build && tsup --watch --config ../tsup.config.ts",
61
+ "prepublish": "pnpm install && pnpm build",
62
+ "__postinstall": "pnpm run build",
63
+ "test": "echo \"Warning: no test specified\" && exit 0"
64
+ }
65
+ }