Skip to content

Commit 5c2374b

Browse files
committed
Revamp file-based data access
- New datasource URL syntax based on ZEP 8 proposal (zarr-developers/zeps#48) - Support for ZIP archives
1 parent 5898236 commit 5c2374b

File tree

581 files changed

+17936
-5397
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

581 files changed

+17936
-5397
lines changed

.github/workflows/build.yml

+9-4
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,16 @@ jobs:
3131
fetch-depth: 0
3232
- uses: actions/setup-node@v4
3333
with:
34-
node-version: 20.x
34+
node-version: 22.x
3535
cache: "npm"
3636
cache-dependency-path: |
3737
package-lock.json
3838
examples/**/package-lock.json
39+
# go needed for fake_gcs_server used by the javascript tests
40+
- name: Setup go
41+
uses: actions/setup-go@v5
42+
with:
43+
go-version: "stable"
3944
- run: npm install
4045
- run: npm run format:fix
4146
- name: Check for dirty working directory
@@ -99,7 +104,7 @@ jobs:
99104
fetch-depth: 0
100105
- uses: actions/setup-node@v4
101106
with:
102-
node-version: 20.x
107+
node-version: 22.x
103108
- name: Set up Python ${{ matrix.python-version }}
104109
uses: actions/setup-python@v5
105110
with:
@@ -146,7 +151,7 @@ jobs:
146151
fetch-depth: 0
147152
- uses: actions/setup-node@v4
148153
with:
149-
node-version: 20.x
154+
node-version: 22.x
150155
cache: "npm"
151156
- name: Set up Python
152157
uses: actions/setup-python@v5
@@ -221,7 +226,7 @@ jobs:
221226
- name: Use Node.js
222227
uses: actions/setup-node@v4
223228
with:
224-
node-version: 20.x
229+
node-version: 22.x
225230
registry-url: "https://registry.npmjs.org"
226231
- uses: actions/download-artifact@v4
227232
with:

.github/workflows/build_preview.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
strategy:
99
matrix:
1010
node-version:
11-
- "20.x"
11+
- "22.x"
1212
runs-on: ubuntu-latest
1313

1414
steps:

.prettierignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
/templates/
22
/python/
33
/third_party/jpgjs/jpg.js
4-
/testdata/*.json
4+
/testdata/**/*.json
5+
zarr.json
56
.parcel-cache
67
dist
78
/lib
89
/docs/_build/
910
/.ruff_cache
11+
package.json
12+
package-lock.json

build_tools/build-package.ts

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ async function buildPackage(options: {
7171
outbase: srcDir,
7272
bundle: false,
7373
outdir: libDir,
74+
target: "es2022",
7475
});
7576

7677
let compilerOptionsFromConfigFile: ts.CompilerOptions = {};

build_tools/update-conditions.ts

+84-80
Original file line numberDiff line numberDiff line change
@@ -17,70 +17,100 @@ const imports: Record<string, any> = {};
1717
imports["#src/third_party/jpgjs/jpg.js"] = "./src/third_party/jpgjs/jpg.js";
1818
imports["#src/*.js"] = "./src/*.ts";
1919
imports["#src/*"] = "./src/*";
20+
imports["#tests/fixtures/msw"] = {
21+
node: "./tests/fixtures/msw_node.ts",
22+
default: "./tests/fixtures/msw_browser.ts",
23+
};
24+
imports["#tests/fixtures/gl"] = {
25+
node: "./tests/fixtures/gl_node.ts",
26+
default: "./tests/fixtures/gl_browser.ts",
27+
};
28+
imports["#tests/*.js"] = "./tests/*.ts";
2029
imports["#testdata/*"] = "./testdata/*";
2130

22-
const datasourceDir = path.resolve(rootDir, "src", "datasource");
23-
const layerDir = path.resolve(rootDir, "src", "layer");
24-
25-
const datasources = (
26-
await fs.promises.readdir(datasourceDir, { withFileTypes: true })
27-
)
28-
.filter((e) => e.isDirectory())
29-
.map((e) => e.name);
30-
31-
const layers = (await fs.promises.readdir(layerDir, { withFileTypes: true }))
32-
.filter((e) => e.isDirectory())
33-
.map((e) => e.name);
34-
35-
const datasourceKeys = {
36-
backend: "backend",
37-
async_computation: "async_computation",
38-
register_default: "frontend",
39-
register_credentials_provider: "frontend",
40-
} as const;
31+
async function listSubdirs(dir: string): Promise<string[]> {
32+
return (await fs.promises.readdir(dir, { withFileTypes: true }))
33+
.filter((e) => e.isDirectory())
34+
.map((e) => e.name);
35+
}
4136

42-
const datasourceModules = Object.fromEntries(
43-
Object.values(datasourceKeys).map((key) => [key, new Array<string>()]),
44-
);
37+
async function writeModule(modulePath: string, imports: string[]) {
38+
await fs.promises.writeFile(
39+
modulePath,
40+
"// DO NOT EDIT: Generated by config/update_conditions.ts\n" +
41+
imports.map((name) => `import ${JSON.stringify(name)};\n`).join(""),
42+
{ encoding: "utf-8" },
43+
);
44+
}
4545

46-
for (const datasource of datasources) {
47-
for (const [filePrefix, moduleKind] of Object.entries(datasourceKeys)) {
48-
const sourcePrefix = `./src/datasource/${datasource}/${filePrefix}`;
49-
if (
50-
await fs.promises
51-
.stat(path.resolve(rootDir, `${sourcePrefix}.ts`))
52-
.catch(() => undefined)
53-
) {
54-
const source = sourcePrefix + JS_EXT;
55-
const conditions: Record<string, string> = {};
56-
if (datasource === "python") {
57-
conditions["neuroglancer/python"] = source;
58-
conditions.default = NOOP;
59-
} else {
60-
if (filePrefix === "register_credentials_provider") {
61-
conditions["neuroglancer/python"] = NOOP;
46+
async function handleDrivers(
47+
kind: string,
48+
moduleMap: Record<string, string[]>,
49+
) {
50+
const driverDir = path.resolve(rootDir, "src", kind);
51+
const drivers = await listSubdirs(driverDir);
52+
const modules: Record<string, string[]> = {};
53+
for (const driver of drivers) {
54+
for (const [filePrefix, moduleKinds] of Object.entries(moduleMap)) {
55+
const sourcePrefix = `./src/${kind}/${driver}/${filePrefix}`;
56+
if (
57+
await fs.promises
58+
.stat(path.resolve(rootDir, `${sourcePrefix}.ts`))
59+
.catch(() => undefined)
60+
) {
61+
const source = sourcePrefix + JS_EXT;
62+
const conditions: Record<string, string> = {};
63+
if (driver === "python") {
64+
conditions["neuroglancer/python"] = source;
65+
conditions.default = NOOP;
66+
} else {
67+
if (filePrefix === "register_credentials_provider") {
68+
conditions["neuroglancer/python"] = NOOP;
69+
}
70+
conditions[`neuroglancer/${kind}/${driver}:enabled`] = source;
71+
conditions[`neuroglancer/${kind}:none_by_default`] = NOOP;
72+
conditions[`neuroglancer/${kind}/${driver}:disabled`] = NOOP;
73+
conditions.default = source;
74+
}
75+
let moduleId = `#${kind}/${driver}`;
76+
if (filePrefix !== "index") {
77+
moduleId += `/${filePrefix}`;
78+
}
79+
imports[moduleId] = conditions;
80+
for (const moduleKind of moduleKinds) {
81+
if (modules[moduleKind] === undefined) {
82+
modules[moduleKind] = [];
83+
}
84+
modules[moduleKind].push(moduleId);
6285
}
63-
conditions[`neuroglancer/datasource/${datasource}:enabled`] = source;
64-
conditions["neuroglancer/datasource:none_by_default"] = NOOP;
65-
conditions[`neuroglancer/datasource/${datasource}:disabled`] = source;
66-
conditions.default = source;
6786
}
68-
const moduleId = `#datasource/${datasource}/${filePrefix}`;
69-
imports[moduleId] = conditions;
70-
datasourceModules[moduleKind].push(moduleId);
7187
}
7288
}
89+
for (const [moduleKind, moduleIds] of Object.entries(modules)) {
90+
await writeModule(
91+
path.resolve(driverDir, `enabled_${moduleKind}_modules.ts`),
92+
moduleIds,
93+
);
94+
}
7395
}
7496

75-
for (const layer of layers) {
76-
const source = `./src/layer/${layer}/index` + JS_EXT;
77-
imports[`#layer/${layer}`] = {
78-
[`neuroglancer/layer/${layer}:enabled`]: source,
79-
"neuroglancer/layer:none_by_default": NOOP,
80-
[`neuroglancer/layer/${layer}:enabled`]: source,
81-
default: source,
82-
};
83-
}
97+
await handleDrivers("datasource", {
98+
backend: ["backend"],
99+
async_computation: ["async_computation"],
100+
register_default: ["frontend"],
101+
register_credentials_provider: ["frontend"],
102+
});
103+
104+
await handleDrivers("kvstore", {
105+
register: ["frontend", "backend"],
106+
register_frontend: ["frontend"],
107+
register_backend: ["backend"],
108+
register_credentials_provider: ["frontend"],
109+
});
110+
111+
await handleDrivers("layer", {
112+
index: ["frontend"],
113+
});
84114

85115
// main entrypoint.
86116
imports["#main"] = {
@@ -94,32 +124,6 @@ imports["#python_integration_build"] = {
94124
default: NOOP,
95125
};
96126

97-
async function writeModule(modulePath: string, imports: string[]) {
98-
await fs.promises.writeFile(
99-
modulePath,
100-
"// DO NOT EDIT: Generated by config/update_conditions.ts\n" +
101-
imports.map((name) => `import ${JSON.stringify(name)};\n`).join(""),
102-
{ encoding: "utf-8" },
103-
);
104-
}
105-
106-
for (const [moduleKind, moduleIds] of Object.entries(datasourceModules)) {
107-
await writeModule(
108-
path.resolve(
109-
rootDir,
110-
"src",
111-
"datasource",
112-
`enabled_${moduleKind}_modules.ts`,
113-
),
114-
moduleIds,
115-
);
116-
}
117-
118-
await writeModule(
119-
path.resolve(rootDir, "src", "layer", "enabled_frontend_modules.ts"),
120-
layers.map((name) => `#layer/${name}`),
121-
);
122-
123127
packageJson.imports = imports;
124128

125129
packageJson.exports = {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* @license
3+
* Copyright 2024 Google Inc.
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { spawnSync } from "node:child_process";
18+
import fs from "node:fs/promises";
19+
import path from "node:path";
20+
21+
export async function getFakeGcsServerBin(): Promise<string> {
22+
const binDir = path.join(
23+
import.meta.dirname,
24+
"..",
25+
"..",
26+
"node_modules",
27+
".cache",
28+
"gobin",
29+
);
30+
const serverBinPath =
31+
path.join(binDir, "fake-gcs-server") +
32+
(process.platform === "win32" ? ".exe" : "");
33+
if (
34+
!(await fs.access(serverBinPath).then(
35+
() => true,
36+
() => false,
37+
))
38+
) {
39+
console.log("Building fake-gcs-server");
40+
// Note: For unknown reasons, using `await promisify(spawn)` in place of
41+
// `spawnSync` causes the vitest process to exit as soon as the child
42+
// process completes.
43+
spawnSync(
44+
"go",
45+
[
46+
"install",
47+
"github.com/fsouza/fake-gcs-server@3b3d059cbaade55b480196a51dedb7aa82ec2b0a",
48+
],
49+
{
50+
env: { ...process.env, GOBIN: binDir },
51+
stdio: ["ignore", "inherit", "inherit"],
52+
},
53+
);
54+
console.log("Done building fake-gcs-server");
55+
}
56+
return serverBinPath;
57+
}

0 commit comments

Comments
 (0)