Skip to content
Draft
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
11 changes: 11 additions & 0 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: Setup
description: Checkout, setup Node, and install dependencies
runs:
using: composite
steps:
- name: Setup Node
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 #v6.2.0
with:
node-version: ${{ env.NODE_VERSION }}
- run: yarn install --frozen-lockfile
shell: bash
49 changes: 35 additions & 14 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,41 @@
name: Setup packages
name: CI
env:
NODE_VERSION: "22.x"

on:
pull_request:
branches:
- main
pull_request:
branches:
- main

jobs:
setup:
name: Install dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: ${{env.NODE_VERSION}}
- run: yarn install --frozen-lockfile
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
- uses: ./.github/actions/setup
- run: npm run lint

format:
name: Format check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
- uses: ./.github/actions/setup
- run: npm run formatCheck
typecheck:
name: Type check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
- uses: ./.github/actions/setup
- run: gatsby build
- run: npm run typeCheck

test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
- uses: ./.github/actions/setup
- run: npm test
8 changes: 8 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"plugins": ["@trivago/prettier-plugin-sort-imports"],
"printWidth": 80,
"tabWidth": 4,
"importOrder": ["^react", "^gatsby", "^[^./]", "^[./]"],
"importOrderSeparation": true,
"importOrderSortSpecifiers": true
}
1 change: 1 addition & 0 deletions eslint-plugin-sort-destructure-keys.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
declare module 'eslint-plugin-sort-destructure-keys';
26 changes: 26 additions & 0 deletions eslint.config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import eslint from '@eslint/js';
import globals from "globals";
import tseslint from "typescript-eslint";
import sortDestructureKeys from "eslint-plugin-sort-destructure-keys";
import { defineConfig } from "eslint/config";

export default defineConfig([
eslint.configs.recommended,
tseslint.configs.recommended,
{
files: ["**/*.{ts,tsx}"],
plugins: {
"sort-destructure-keys": sortDestructureKeys,
},
languageOptions: { globals: { ...globals.browser, ...globals.node } },
rules: {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error"],
"sort-destructure-keys/sort-destructure-keys": [
2,
{ caseSensitive: false },
],
"@typescript-eslint/no-require-imports": "off",
},
},
]);
16 changes: 7 additions & 9 deletions gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,7 @@ const read = (p) => fs.readFileSync(path.join(__dirname, p), "utf8");
* They serve as single source of truth, can be added/edited via CMS,
* and are referenced by other markdown files.
*/
const DATA_ONLY_PAGES = [
"software",
"dataset",
"allenite",
"program",
];
const DATA_ONLY_PAGES = ["software", "dataset", "allenite", "program"];

exports.createSchemaCustomization = ({ actions, schema }) => {
const { createTypes } = actions;
Expand Down Expand Up @@ -85,7 +80,7 @@ exports.createResolvers = ({ createResolvers }) => {
resolve: (source) =>
stringWithDefault(
source.description,
"No description provided."
"No description provided.",
),
},
title: {
Expand All @@ -106,7 +101,10 @@ exports.createResolvers = ({ createResolvers }) => {
return current;
}

const resolvedDatasetSlug = resolveSlug(raw.dataset, DATASET_PATH);
const resolvedDatasetSlug = resolveSlug(
raw.dataset,
DATASET_PATH,
);
current.dataset = resolvedDatasetSlug;
current.cellLines = resolveToArray(raw.cellLines);
current.protocols = resolveToArray(raw.protocols);
Expand Down Expand Up @@ -173,7 +171,7 @@ exports.createPages = ({ actions, graphql }) => {
path: edge.node.fields.slug,
tags: edge.node.frontmatter.tags,
component: path.resolve(
`src/templates/${String(templateKey)}.tsx`
`src/templates/${String(templateKey)}.tsx`,
),
// additional data can be passed via context
context: {
Expand Down
2 changes: 1 addition & 1 deletion gatsbyutils/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ const SOFTWARE_PATH = `software`;
module.exports = {
DATASET_PATH,
SOFTWARE_PATH,
};
};
6 changes: 3 additions & 3 deletions gatsbyutils/gatsby-resolver-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const resolveSoftwareTools = (rawSoftware) => {
softwareTool: resolveSlug(item.softwareTool, SOFTWARE_PATH),
customDescription: stringWithDefault(
item.customDescription,
null
null,
),
};
}
Expand All @@ -66,7 +66,7 @@ const resolveSlug = (id, directory) => {
if (!id) return null;
const slugPart = slugify(id, { lower: true, strict: true }).replace(
/^\/+|\/+$/g,
""
"",
); // Slugify and remove leading/trailing slashes
return `/${directory}/${slugPart}/`;
};
Expand All @@ -76,4 +76,4 @@ module.exports = {
resolveToArray,
resolveSlug,
resolveSoftwareTools,
};
};
87 changes: 45 additions & 42 deletions gatsbyutils/test/gatsby-resolver-utils.test.js
Original file line number Diff line number Diff line change
@@ -1,109 +1,112 @@
import { describe, it, expect } from 'vitest';
import { resolveSlug, resolveSoftwareTools } from '../gatsby-resolver-utils';
import { DATASET_PATH, SOFTWARE_PATH } from '../constants';
import { describe, expect, it } from "vitest";

describe('resolveSlug', () => {
it('should return null when id is falsy', () => {
expect(resolveSlug(null, 'software')).toBe(null);
expect(resolveSlug(undefined, 'software')).toBe(null);
expect(resolveSlug('', 'software')).toBe(null);
import { DATASET_PATH, SOFTWARE_PATH } from "../constants";
import { resolveSlug, resolveSoftwareTools } from "../gatsby-resolver-utils";

describe("resolveSlug", () => {
it("should return null when id is falsy", () => {
expect(resolveSlug(null, "software")).toBe(null);
expect(resolveSlug(undefined, "software")).toBe(null);
expect(resolveSlug("", "software")).toBe(null);
});

it('should build a slug from id and directory', () => {
it("should build a slug from id and directory", () => {
expect(resolveSlug("released-emt-dataset", DATASET_PATH)).toBe(
"/dataset/released-emt-dataset/"
"/dataset/released-emt-dataset/",
);
});

it('should slugify the id to lowercase', () => {
it("should slugify the id to lowercase", () => {
expect(resolveSlug("UPPERCASE", DATASET_PATH)).toBe(
"/dataset/uppercase/"
"/dataset/uppercase/",
);
});

it('should handle special characters in id', () => {
it("should handle special characters in id", () => {
expect(resolveSlug("Tool & Library", SOFTWARE_PATH)).toBe(
"/software/tool-and-library/"
"/software/tool-and-library/",
);
expect(resolveSlug("Some/Path/Name", SOFTWARE_PATH)).toBe(
"/software/somepathname/"
"/software/somepathname/",
);
});

it('should handle ids with leading/trailing spaces', () => {
expect(resolveSlug(' trimmed ', 'software')).toBe('/software/trimmed/');
it("should handle ids with leading/trailing spaces", () => {
expect(resolveSlug(" trimmed ", "software")).toBe(
"/software/trimmed/",
);
});
});

describe('resolveSoftwareTools', () => {
it('should return empty array for non-array inputs', () => {
describe("resolveSoftwareTools", () => {
it("should return empty array for non-array inputs", () => {
expect(resolveSoftwareTools(null)).toEqual([]);
expect(resolveSoftwareTools(undefined)).toEqual([]);
expect(resolveSoftwareTools('string')).toEqual([]);
expect(resolveSoftwareTools("string")).toEqual([]);
expect(resolveSoftwareTools({})).toEqual([]);
expect(resolveSoftwareTools(123)).toEqual([]);
});

it('should return empty array for empty array', () => {
it("should return empty array for empty array", () => {
expect(resolveSoftwareTools([])).toEqual([]);
});

it('should filter out invalid items', () => {
const input = [null, undefined, 'string', {}, { other: 'prop' }];
it("should filter out invalid items", () => {
const input = [null, undefined, "string", {}, { other: "prop" }];
expect(resolveSoftwareTools(input)).toEqual([]);
});

it('should transform valid items with softwareTool', () => {
const input = [{ softwareTool: 'Simularium' }];
it("should transform valid items with softwareTool", () => {
const input = [{ softwareTool: "Simularium" }];
expect(resolveSoftwareTools(input)).toEqual([
{
softwareTool: '/software/simularium/',
softwareTool: "/software/simularium/",
customDescription: null,
},
]);
});

it('should preserve customDescription when provided', () => {
it("should preserve customDescription when provided", () => {
const input = [
{
softwareTool: 'Simularium',
customDescription: 'Custom description here',
softwareTool: "Simularium",
customDescription: "Custom description here",
},
];
expect(resolveSoftwareTools(input)).toEqual([
{
softwareTool: '/software/simularium/',
customDescription: 'Custom description here',
softwareTool: "/software/simularium/",
customDescription: "Custom description here",
},
]);
});

it('should return null for empty customDescription', () => {
const input = [{ softwareTool: 'Simularium', customDescription: '' }];
it("should return null for empty customDescription", () => {
const input = [{ softwareTool: "Simularium", customDescription: "" }];
expect(resolveSoftwareTools(input)).toEqual([
{
softwareTool: '/software/simularium/',
softwareTool: "/software/simularium/",
customDescription: null,
},
]);
});

it('should handle mixed valid and invalid items', () => {
it("should handle mixed valid and invalid items", () => {
const input = [
null,
{ softwareTool: 'Simularium' },
{ other: 'invalid' },
{ softwareTool: 'TFE', customDescription: 'Time explorer' },
{ softwareTool: "Simularium" },
{ other: "invalid" },
{ softwareTool: "TFE", customDescription: "Time explorer" },
];
expect(resolveSoftwareTools(input)).toEqual([
{
softwareTool: '/software/simularium/',
softwareTool: "/software/simularium/",
customDescription: null,
},
{
softwareTool: '/software/tfe/',
customDescription: 'Time explorer',
softwareTool: "/software/tfe/",
customDescription: "Time explorer",
},
]);
});
});
});
15 changes: 12 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,31 @@
"build": "npm run clean && gatsby build",
"develop": "npm run clean && gatsby develop",
"serve": "gatsby serve",
"format": "prettier --trailing-comma es5 --no-semi --single-quote --write \"{gatsby-*.js,src/**/*.js}\"",
"format": "prettier \"{src,gatsbyutils,netlify}/**/*.{ts,tsx,js,jsx}\" gatsby-*.js --write",
"formatCheck": "prettier \"{src,gatsbyutils,netlify}/**/*.{ts,tsx,js,jsx}\" gatsby-*.js --check",
"test": "vitest",
"dev": "npx concurrently \"npx netlify-cms-proxy-server\" \"npm start -- --progress\""
"dev": "npx concurrently \"npx netlify-cms-proxy-server\" \"npm start -- --progress\"",
"lint": "eslint \"src/**/*.{ts,tsx}\" --quiet --fix",
"typeCheck": "npx tsc --noEmit"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"@types/antd": "^1.0.0",
"@types/node": "^20.11.20",
"@types/react": "^18.2.59",
"@types/react-dom": "^18.2.19",
"@types/react-helmet": "^6.1.11",
"concurrently": "^9.2.1",
"eslint": "^9.38.0",
"eslint-plugin-sort-destructure-keys": "^3.0.0",
"gatsby-plugin-postcss": "^6.13.1",
"jiti": "^2.6.1",
"netlify-cli": "^17.15.7",
"postcss": "^8.4.35",
"prettier": "^2.0.5",
"prettier": "^3.0.0",
"typescript": "^5.3.3",
"typescript-eslint": "^8.56.0",
"typescript-plugin-css-modules": "^5.1.0"
},
"engines": {
Expand Down
Loading