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
4 changes: 3 additions & 1 deletion .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ jobs:
cache: npm
- run: npm ci
- name: Enforce code style
run: npx prettier --list-different "src/**/*.[jt]s" "tests/**/*.[jt]s" "src/**/*.vue"
run: npx prettier --list-different "src/**/*.[jt]s" "tests/**/*.[jt]s" "src/**/*.vue" "packages/dicom/**/*.{ts,mjs,json,md}"
- name: Lint
run: npm run lint
- name: Build independent DICOM consumers
run: npm run dicom:check
- name: Quality ratchets
# A file may not get worse than it is on the pull request's target branch.
if: github.event_name == 'pull_request'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/e2e-datasets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
uses: actions/cache@v4
with:
path: .tmp/datasets
key: e2e-datasets-${{ hashFiles('tests/datasets.ts') }}
key: e2e-datasets-${{ hashFiles('tests/datasets.ts', 'tests/dicomVolumeCorpus.json', 'tests/dicomCompositionCorpus.json') }}
restore-keys: e2e-datasets-
- name: Download missing datasets
run: node tests/downloadDatasets.ts
2 changes: 1 addition & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
uses: actions/cache@v4
with:
path: .tmp/datasets
key: e2e-datasets-${{ hashFiles('tests/datasets.ts') }}
key: e2e-datasets-${{ hashFiles('tests/datasets.ts', 'tests/dicomVolumeCorpus.json', 'tests/dicomCompositionCorpus.json') }}
restore-keys: e2e-datasets-
- name: Run E2E on chrome (linux)
if: matrix.os == 'ubuntu-latest'
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,5 @@ reports
.github/copilot
CLAUDE.md
.claude/

/packages/dicom/dist/
1 change: 0 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
dist
node_modules
public
src/io/itk-dicom
src/io/resample
*.json
71 changes: 58 additions & 13 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import globals from 'globals';
// not a dependency of this repo, and the built-in rule expresses the same
// zones with no new dependency. Two zones per feature:
// 1. Code OUTSIDE the feature directory may reach it ONLY through its public
// surface `@/src/<dir>` (the index), never a deep path.
// modules listed in its public surface.
// 2. The feature's pure layer may not import stores, components, or the
// upper feature modules, and stays framework-free (no pinia, no vue) —
// dependencies point downward only.
Expand Down Expand Up @@ -64,16 +64,27 @@ const ownFeatureExceptPure = (feature) => {
const upperLayerMessage = (feature) =>
`The ${feature.dir} pure layer must not import stores, components, or upper feature modules. Dependencies point downward only.`;

const dicomPublicSurface = {
regex: '^@/packages/dicom/(?!core/.+|vtk/.+)',
message: 'Import the defining DICOM core or vtk module directly.',
};

const featureBoundaries = (features) => {
const publicSurface = ({ dir }) => ({
group: [`@/src/${dir}/*`, `@/src/${dir}/*/**`, `!@/src/${dir}/index`],
message: `Import the ${dir} feature only from its public surface \`@/src/${dir}\` (src/${dir}/index.ts). A deep import bypasses the feature boundary.`,
const publicSurface = ({ dir, publicModules }) => ({
group: [
`@/src/${dir}/*`,
`@/src/${dir}/*/**`,
...publicModules.map((module) => `!@/src/${dir}/${module}`),
],
message: `Import the ${dir} feature only through its defining public modules: ${publicModules.join(', ')}.`,
});
const publicFeatures = features.filter(
(feature) => feature.publicSurface !== false
);
const otherSurfaces = (feature) =>
publicFeatures.filter((other) => other !== feature).map(publicSurface);
const otherSurfaces = (feature) => [
dicomPublicSurface,
...publicFeatures.filter((other) => other !== feature).map(publicSurface),
];

return [
{
Expand All @@ -82,7 +93,12 @@ const featureBoundaries = (features) => {
rules: {
'no-restricted-imports': [
'error',
{ patterns: publicFeatures.map(publicSurface) },
{
patterns: [
dicomPublicSurface,
...publicFeatures.map(publicSurface),
],
},
],
},
},
Expand Down Expand Up @@ -149,7 +165,6 @@ const E2E_TIMEOUT_DEBT = [
'tests/specs/cine-ruler-session.e2e.ts',
'tests/specs/crosshairs-multi-image.e2e.ts',
'tests/specs/delete-selected-annotation.e2e.ts',
'tests/specs/dicom-dimension-mismatch.e2e.ts',
'tests/specs/different-direction-labelmap.e2e.ts',
'tests/specs/imageCacheUtils.ts',
'tests/specs/layer-source-lifetime.e2e.ts',
Expand All @@ -172,10 +187,10 @@ const E2E_TIMEOUT_DEBT = [
export default tseslint.config(
{
ignores: [
'src/io/itk-dicom/emscripten-build/**',
'src/io/resample/emscripten-build/**',
'**/*.d.ts',
'dist/**',
'packages/dicom/dist/**',
'node_modules/**',
],
},
Expand Down Expand Up @@ -247,9 +262,10 @@ export default tseslint.config(
'no-restricted-syntax': [
'error',
{
selector: "Property[key.name='timeout']",
selector:
"Property[key.name='timeout']:not([value.type='Identifier'][value.name='DOWNLOAD_TIMEOUT'])",
message:
'Wait for a state the page publishes rather than passing a timeout.',
'Wait for a state the page publishes; use DOWNLOAD_TIMEOUT for data-loading readiness.',
},
{
selector:
Expand Down Expand Up @@ -293,7 +309,7 @@ export default tseslint.config(
},
},
{
files: ['src/vtk/**/*.{js,ts}'],
files: ['src/vtk/**/*.{js,ts}', 'packages/dicom/vtk/**/*.{js,ts}'],
rules: {
'no-param-reassign': [
'error',
Expand Down Expand Up @@ -336,6 +352,7 @@ export default tseslint.config(
},
{
dir: 'processing',
publicModules: ['integration', 'store', 'types'],
pure: [
'src/processing/engine/**/*.{js,ts}',
'src/processing/types.ts',
Expand All @@ -344,6 +361,12 @@ export default tseslint.config(
},
{
dir: 'referenceLines',
publicModules: [
'ReferenceLines.vue',
'ReferenceLineSegments.vue',
'geometry',
'store',
],
pure: [
'src/referenceLines/geometry.ts',
'src/referenceLines/crossings.ts',
Expand Down Expand Up @@ -372,8 +395,9 @@ export default tseslint.config(
// level because the ratchet only fails a file whose debt grows, so existing
// files stay over these numbers without failing `npm run lint`.
{
files: ['src/**/*.{js,ts,vue}'],
files: ['src/**/*.{js,ts,vue}', 'packages/dicom/**/*.{js,ts}'],
ignores: [
'packages/dicom/**/__tests__/**',
'src/**/__tests__/**',
'src/**/*.{spec,test}.{js,ts}',
'src/**/*.d.ts',
Expand All @@ -389,5 +413,26 @@ export default tseslint.config(
],
},
},
...['core', 'vtk'].map((entry) => ({
files: [`packages/dicom/${entry}/**/*.{js,ts}`],
ignores: ['**/__tests__/**'],
rules: {
'no-restricted-imports': [
'error',
{
patterns: [
{
regex:
entry === 'core'
? '^(@/|@src/|vue(?:/|$)|pinia(?:/|$)|@vueuse/|@kitware/vtk\\.js(?:/|$))'
: '^(@/|@src/|vue(?:/|$)|pinia(?:/|$)|@vueuse/)',
message:
'DICOM package internals use relative imports and cannot depend on application state or UI frameworks. Core cannot depend on vtk.js.',
},
],
},
],
},
})),
eslintConfigPrettier
);
97 changes: 97 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 12 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
"files": [
"dist",
"src",
"backend-contract"
"backend-contract",
"packages/dicom/core/**/*.ts",
"packages/dicom/vtk/**/*.ts",
"packages/dicom/README.md",
"!packages/dicom/**/__tests__/**"
],
"scripts": {
"prepare": "node scripts/apply-patches.mjs",
Expand All @@ -22,17 +26,17 @@
"test:e2e:chrome": "cross-env VITE_SHOW_SAMPLE_DATA=true npm run build && cross-env HEADED=0 wdio run ./wdio.chrome.conf.ts",
"test:e2e:chrome:skip-build": "cross-env HEADED=0 wdio run ./wdio.chrome.conf.ts",
"test:e2e:dev": "cross-env HEADED=0 VITE_SHOW_SAMPLE_DATA=true concurrently -P \"npm run dev\" \"wdio run ./wdio.dev.conf.ts --watch {@}\"",
"lint": "vue-tsc --noEmit && eslint \"src/**/*.{js,ts,vue}\" \"tests/**/*.{js,ts}\" \"scripts/**/*.mjs\"",
"build:all": "npm run build:dicom && npm run build:resample && npm run build",
"build:dicom": "itk-wasm -s src/io/itk-dicom/ build ",
"build:dicom:debug": "itk-wasm -s src/io/itk-dicom/ build -- -DCMAKE_BUILD_TYPE=Debug",
"lint": "vue-tsc --noEmit && eslint \"src/**/*.{js,ts,vue}\" \"tests/**/*.{js,ts}\" \"scripts/**/*.mjs\" \"packages/dicom/**/*.{js,ts,mjs}\"",
"build:all": "npm run build:resample && npm run build",
"build:resample": "itk-wasm -s src/io/resample/ build ",
"build:resample:debug": "itk-wasm -s src/io/resample/ build -- -DCMAKE_BUILD_TYPE=Debug",
"build:watch": "npm run build -- --watch",
"prettify": "prettier --write src tests",
"prettify": "prettier --write src tests packages/dicom",
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs"
"docs:preview": "vitepress preview docs",
"dicom:build": "node packages/dicom/scripts/build.mjs",
"dicom:check": "npm run dicom:build && node packages/dicom/scripts/check-consumers.mjs"
},
"devDependencies": {
"@commitlint/cli": "^21.0.0",
Expand Down Expand Up @@ -73,6 +77,7 @@
"core-js": "3.47.0",
"cors": "^2.8.5",
"cross-env": "^10.1.0",
"dcmjs": "^0.52.0",
"dicom-parser": "^1.8.21",
"dicomweb-client-typed": "^0.8.6",
"eslint": "^9.39.1",
Expand Down
Loading
Loading