Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
# Glide Code Columns

Code columns are an open source way to create computations using your data in [Glide Apps](https://glideapps.com)
Code columns are an open source way to create computations using your data in [Glide Apps](https://glideapps.com).

## How glide consumes columns

The glide main app loads columns as static JS bundles served from this repo's Vercel deployment — **not** as an npm package. Each column is independently bundled by esbuild and served at:

| URL | Description |
|-----|-------------|
| `/{slug}/index.js` | CJS bundle (CommonJS, no React) |
| `/{slug}/function.js` | ESM bundle |
| `/{slug}/glide.json` | Column metadata (name, params, result type) |
| `/all.json` | Master manifest of all columns |

Column bundles communicate with the glide host app via `window.postMessage()`. The bundles have no React dependency and are fully independent of the Next.js demo site.

### Caching

Column bundles (`index.js`, `function.js`, `glide.json`) are immutably cached at the CDN — content is tied to a specific Vercel deployment. `/all.json` uses a shorter cache with stale-while-revalidate to reflect newly added columns sooner.

## Develop

Expand Down
4 changes: 2 additions & 2 deletions next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
/// <reference types="next/image-types/global" />
import "./.next/types/routes.d.ts";

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.
6,385 changes: 3,087 additions & 3,298 deletions package-lock.json

Large diffs are not rendered by default.

26 changes: 13 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"main": "index.js",
"scripts": {
"manifest": "script/build.sh",
"build": "npm test && npm run manifest && NODE_OPTIONS=--openssl-legacy-provider next build",
"build": "npm test && npm run manifest && next build --webpack",
"export": "npm run manifest",
"dev": "jest --watchAll & next dev",
"start": "npm run dev",
Expand All @@ -32,32 +32,32 @@
"devDependencies": {
"@types/chroma-js": "^2.1.3",
"@types/humanize-plus": "^1.8.0",
"@types/jest": "^29.5.14",
"@types/jest": "^30.0.0",
"@types/jsbarcode": "^3.11.1",
"@types/lodash": "^4.14.175",
"@types/luxon": "^2.0.5",
"@types/marked": "^3.0.2",
"@types/qrcode-svg": "^1.1.1",
"@types/react": "^17.0.40",
"@types/react-dom": "^17.0.26",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@types/seedrandom": "^3.0.1",
"autoprefixer": "^10.3.4",
"esbuild": "^0.13.4",
"esbuild": "^0.28.0",
"husky": "^7.0.4",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"jest": "^30.0.0",
"jest-environment-jsdom": "^30.0.0",
"postcss": "^8.3.6",
"prettier": "^2.4.1",
"tailwindcss": "^2.2.15",
"ts-jest": "^29.4.9",
"ts-node": "^10.2.1",
"typescript": "^4.4.2",
"typescript": "^5.0.0",
"vercel": "^28.2.2"
},
"dependencies": {
"@heroicons/react": "^2.0.16",
"@jcubic/lips": "^1.0.0-beta.14",
"axios": "^0.23.0",
"axios": "^1.0.0",
"bayes": "^1.0.0",
"cheerio": "^1.0.0-rc.10",
"chroma-js": "^2.1.2",
Expand All @@ -71,7 +71,7 @@
"humanize-plus": "^1.8.2",
"hyperformula": "^1.2.0",
"iframe-resizer": "^4.3.2",
"javascript-time-ago": "^2.3.9",
"javascript-time-ago": "~2.3.9",
"jq-web": "^0.5.1",
"jsbarcode": "^3.11.5",
"lodash": "^4.17.21",
Expand All @@ -81,10 +81,10 @@
"mathjs": "^9.5.1",
"mini-svg-data-uri": "^1.4.4",
"nanoid": "^3.1.30",
"next": "^11.1.2",
"next": "^16.0.0",
"qrcode-svg": "^1.1.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"rrule": "^2.6.8",
"seedrandom": "^3.0.5",
"wink-eng-lite-web-model": "^1.2.2",
Expand Down
1 change: 1 addition & 0 deletions script/manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ const {
} = require("./index");

fs.writeFileSync(`${__dirname}/glide.json`, json);
process.exit(0);
25 changes: 13 additions & 12 deletions src/columns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,27 @@ export function getColumnSlugs(): string[] {
.map(p => p.replace(".ts", ""));
}

// Synchronous version — works in Jest/ts-jest where ESM deps are transpiled to CJS.
export function getColumnDefinition(slug: string): ColumnDefinition<any> {
const { default: manifest } = require(`${__dirname}/columns/${slug}`) as {
default: ColumnDefinition<any>;
};
return manifest;
}

export function getColumnDefinitions(): Record<string, ColumnDefinition<any>> {
const defs = {};
const slugs = getColumnSlugs();
for (const slug of slugs) {
defs[slug] = getColumnDefinition(slug);
}
return defs;
// Async version — works in Next.js webpack server context where ESM packages
// may be compiled as async modules (using import() properly awaits them).
export async function getColumnDefinitionAsync(slug: string): Promise<ColumnDefinition<any>> {
const { default: manifest } = await import(`./columns/${slug}`) as {
default: ColumnDefinition<any>;
};
return manifest;
}

export function getColumnManifests(): Record<string, Manifest> {
const definitions = getColumnDefinitions();
const manifests = Object.fromEntries(
Object.entries(definitions).map(([slug, def]) => [slug, toStrictManifest(def)])
export async function getColumnManifests(): Promise<Record<string, Manifest>> {
const slugs = getColumnSlugs();
const pairs = await Promise.all(
slugs.map(async slug => [slug, toStrictManifest(await getColumnDefinitionAsync(slug))] as const)
);
return manifests;
return Object.fromEntries(pairs);
}
2 changes: 1 addition & 1 deletion src/components/REPL.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import "iframe-resizer";

export * from "../glide";

const REPL: React.VFC<ColumnDefinition<any>> = props => {
const REPL: React.FC<ColumnDefinition<any>> = props => {
const {
params,
result: { type: resultType },
Expand Down
2 changes: 1 addition & 1 deletion src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface Props {
}

export const getStaticProps: GetStaticProps<Props> = async () => {
const manifests = getColumnManifests();
const manifests = await getColumnManifests();
return {
props: {
manifests: removeUndefineds(manifests),
Expand Down
12 changes: 10 additions & 2 deletions src/pages/preview/[slug].tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { GetStaticPaths, GetStaticProps } from "next";
import { getColumnSlugs, getColumnDefinition } from "../../columns";
import { useState, useEffect } from "react";
import { getColumnSlugs, getColumnDefinitionAsync } from "../../columns";
import { ColumnDefinition } from "../../glide";
import REPL from "../../components/REPL";

interface Props {
Expand All @@ -24,7 +26,13 @@ export const getStaticProps: GetStaticProps<Props> = async ({ params }) => {

const PreviewPage = (props: Props) => {
const { slug } = props;
const manifest = getColumnDefinition(slug);
const [manifest, setManifest] = useState<ColumnDefinition<any> | null>(null);

useEffect(() => {
getColumnDefinitionAsync(slug).then(setManifest);
}, [slug]);

if (!manifest) return null;
return <REPL key={slug} {...manifest} />;
};

Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"jsx": "react-jsx",
"allowSyntheticDefaultImports": true,
"incremental": true
},
Expand Down
56 changes: 56 additions & 0 deletions vercel.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"headers": [
{
"source": "/:slug/index.js",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
},
{
"key": "CDN-Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
},
{
"source": "/:slug/function.js",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
},
{
"key": "CDN-Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
},
{
"source": "/:slug/glide.json",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
},
{
"key": "CDN-Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
},
{
"source": "/all.json",
"headers": [
{
"key": "Cache-Control",
"value": "public, s-maxage=86400, stale-while-revalidate=3600"
},
{
"key": "CDN-Cache-Control",
"value": "public, s-maxage=86400, stale-while-revalidate=3600"
}
]
}
]
}
Loading