feat(cli): Ohif cli for modes and extensions modification (#2696)
* feat: Add initial cli tool structure * feat: add copying template files * feat: Add mode template and command * feat: Add readme template generation * feat: Add documentation to extension template * feat: Enhance documentation of the template mode * fix: cli module type * feat: Add config-based mode and extension registration (#2660) * feat: Add ohif cli add/remove extension/mode (#2661) * Basic working CLI for add-extension and remove-extension * Basic cli for add/remove extension/mode, lots more to do. * Cleanup and harden] * feat: Add list of tasks to add-mode Co-authored-by: Alireza <ar.sedghi@gmail.com> * feat: Add git initialization for the mode or extension template (#2662) * fix: package json file to include templates * feat: Add git initialization for the mode or extension template * feat: Add more checks of git and target dir * feat: refactore library utilities * feat: Add the list command to print extensions and modes (#2664) * feat: Add the list command to print extensions and modes * Add todo * Feat/ohif cli validation + auto install (#2671) * WIP * Working mode keyword verification * Validation * auto install extensions based on modes * WIP remove unused extensions on removeMove * Working add-mode, remove-mode automatic extension management. * If extension is in used by a mode, don't allow the CLI to uninstall it * Cleanup addExtension * cleanup removeExtension and addMode * Cleanup removeMode * Update existing extensions with the needed keywords/peer deps * Fix broken config * Feat/cli search (#2677) * feat: refactor pretty print for console * feat: add search for modes and extensions * fix: ugly colors * Feat/ohif cli error handling publishing (#2679) * WIP * fix: webpack imports * wip * fix: react router dom private routes * from last commit * wip * fix: webpack prod builds * WIP * Working regsitration with new IDs * Stable Co-authored-by: Alireza <ar.sedghi@gmail.com> * verify extensions when constructing modes. (#2681) * verify extensions when constructing modes. * Add version to unit tests so it conforms to schema * Update ohif utils exposed via @ohif/core * Fix import * fix tests * feat: ohif-cli link local modes/extensions for development (#2682) * feat: enable cli to work with project root * feat: add initial link package * feat: add link and unlink extension * feat: add link and unlink mode * erro handling for link-package * feat: add comment on ohif-cli linking for development (#2686) * Docs/ohif cli (#2687) * feat: Add documentation for templates * feat: Add more documentation * Fix/core publish (#2685) * versions * wip * remove webpack clean output * fix publish * use next as dist tag for v3 for now * fix webpack pro recipe for output * fix: lerna publish next * fix(cli): fix issues when trying to link an extension or a mode (#2725) The generated package.json doesn't contain keywords property which is required by the linkPackage function. The module apth wasn't correclty handled too, and when there is no pluginOptions, it fails while reading the file or while generating a default configuration. * make dicom pdf and video work after cli merge * add axios dependency * comment out the chdir for now * create id and version based on user inputs * customizable path for extension and modes * fix template to make the template mode load * fix the questions to loop if path is not desirable * fix templates * correct package json order * unify the package creation for extension and mode * bump versions for each package * bump extension versions to 3.0 * add gitignore to templates * fix version requirements when ^ * update docs * update docs and fix tests * try to fix the tests * bump node version * remove the version from extensions * remove the version from modes * remove version from extensionManager * fix eslint * revert husky version * fix eslint * fix node version for new eslint * fix documentatoin removing version * fix cicle ci image version * fix circle ci node image * fix circle ci node image * add back the video and pdf Co-authored-by: Matthis Duclos <matthis.duclos@gmail.com> Co-authored-by: James A. Petts <jamesapetts@gmail.com>
This commit is contained in:
parent
82df3eac03
commit
7f0ad6f84d
@ -24,7 +24,7 @@ executors:
|
||||
chrome-and-pacs:
|
||||
docker:
|
||||
# Primary container image where all steps run.
|
||||
- image: 'cypress/browsers:node14.15.0-chrome86-ff82'
|
||||
- image: 'cypress/browsers:node14.17.0-chrome88-ff89'
|
||||
|
||||
defaults: &defaults
|
||||
docker:
|
||||
@ -181,7 +181,7 @@ jobs:
|
||||
|
||||
DEPLOY_TO_DEV:
|
||||
docker:
|
||||
- image: circleci/node:14.15.0
|
||||
- image: circleci/node:14.17.0
|
||||
environment:
|
||||
TERM: xterm
|
||||
NETLIFY_SITE_ID: 32708787-c9b0-4634-b50f-7ca41952da77
|
||||
@ -196,7 +196,7 @@ jobs:
|
||||
|
||||
DEPLOY_TO_STAGING:
|
||||
docker:
|
||||
- image: circleci/node:14.15.0
|
||||
- image: circleci/node:14.17.0
|
||||
environment:
|
||||
TERM: xterm
|
||||
NETLIFY_SITE_ID: c7502ae3-b150-493c-8422-05701e44a969
|
||||
@ -211,7 +211,7 @@ jobs:
|
||||
|
||||
DEPLOY_TO_PRODUCTION:
|
||||
docker:
|
||||
- image: circleci/node:14.15.0
|
||||
- image: circleci/node:14.17.0
|
||||
environment:
|
||||
TERM: xterm
|
||||
NETLIFY_SITE_ID: 79c4a5da-5c95-4dc9-84f7-45fd9dfe21b0
|
||||
@ -263,7 +263,7 @@ jobs:
|
||||
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/repo/.npmrc
|
||||
- run: npx lerna version
|
||||
- run: yarn run build:package-all
|
||||
- run: npx lerna publish from-package
|
||||
- run: yarn run lerna:publish
|
||||
- run: npx lerna run ci:generateSuccessVersion --stream
|
||||
- persist_to_workspace:
|
||||
root: ~/repo
|
||||
@ -367,12 +367,13 @@ workflows:
|
||||
executor: cypress/browsers-chrome76
|
||||
browser: chrome
|
||||
pre-steps:
|
||||
- run: 'rm -rf ~/.yarn && yarn -v && yarn global
|
||||
add wait-on'
|
||||
- run: 'rm -rf ~/.yarn && yarn -v && yarn global add wait-on'
|
||||
yarn: true
|
||||
store_artifacts: false
|
||||
working_directory: platform/viewer
|
||||
build: yarn test:data && npx cross-env QUICK_BUILD=true APP_CONFIG=config/dicomweb-server.js yarn run build
|
||||
build:
|
||||
yarn test:data && npx cross-env QUICK_BUILD=true
|
||||
APP_CONFIG=config/dicomweb-server.js yarn run build
|
||||
# start server --> verify running --> percy + chrome + cypress
|
||||
command: yarn run test:e2e:dist
|
||||
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
|
||||
@ -434,7 +435,9 @@ workflows:
|
||||
yarn: true
|
||||
store_artifacts: false
|
||||
working_directory: platform/viewer
|
||||
build: npx cross-env QUICK_BUILD=true APP_CONFIG=config/e2e.js yarn run build
|
||||
build:
|
||||
npx cross-env QUICK_BUILD=true APP_CONFIG=config/e2e.js yarn run
|
||||
build
|
||||
# start server --> verify running --> percy + chrome + cypress
|
||||
command: yarn run test:e2e:dist
|
||||
cache-key: 'yarn-packages-{{ checksum "yarn.lock" }}'
|
||||
|
||||
4
.eslintignore
Normal file
4
.eslintignore
Normal file
@ -0,0 +1,4 @@
|
||||
config/**
|
||||
docs/**
|
||||
img/**
|
||||
node_modules
|
||||
@ -12,12 +12,7 @@
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
|
||||
},
|
||||
"rules": {
|
||||
"react/jsx-props-no-spreading": "error",
|
||||
"react-hooks/exhaustive-deps": "false",
|
||||
"import/no-unused-modules": [1, {"unusedExports": true}]
|
||||
}
|
||||
},
|
||||
"globals": {
|
||||
"cy": true,
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -36,3 +36,6 @@ screenshots/
|
||||
|
||||
# Locize settings
|
||||
.locize
|
||||
|
||||
# autogenerated files
|
||||
platform/viewer/src/pluginImports.js
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"name": "root",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"node": ">=14",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1.16.0"
|
||||
},
|
||||
|
||||
1
.prettierignore
Normal file
1
.prettierignore
Normal file
@ -0,0 +1 @@
|
||||
*.md
|
||||
@ -1,7 +1,9 @@
|
||||
const autoprefixer = require('autoprefixer');
|
||||
const path = require('path');
|
||||
const tailwindcss = require('tailwindcss');
|
||||
const tailwindConfigPath = path.resolve('tailwind.config.js');
|
||||
const tailwindConfigPath = path.resolve(
|
||||
'../../platform/viewer/tailwind.config.js'
|
||||
);
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const devMode = process.env.NODE_ENV !== 'production';
|
||||
|
||||
|
||||
@ -3,17 +3,20 @@ const dotenv = require('dotenv');
|
||||
//
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
|
||||
// ~~ PLUGINS
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
|
||||
.BundleAnalyzerPlugin;
|
||||
const TerserJSPlugin = require('terser-webpack-plugin');
|
||||
const CopyPlugin = require('copy-webpack-plugin');
|
||||
|
||||
// ~~ PackageJSON
|
||||
const PACKAGE = require('../platform/viewer/package.json');
|
||||
// ~~ RULES
|
||||
const loadShadersRule = require('./rules/loadShaders.js');
|
||||
const loadWebWorkersRule = require('./rules/loadWebWorkers.js');
|
||||
const transpileJavaScriptRule = require('./rules/transpileJavaScript.js');
|
||||
const cssToJavaScript = require('./rules/cssToJavaScript.js');
|
||||
// ~~ PLUGINS
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
|
||||
.BundleAnalyzerPlugin;
|
||||
const TerserJSPlugin = require('terser-webpack-plugin');
|
||||
const CopyPlugin = require('copy-webpack-plugin');
|
||||
|
||||
// ~~ ENV VARS
|
||||
const NODE_ENV = process.env.NODE_ENV;
|
||||
@ -39,16 +42,16 @@ module.exports = (env, argv, { SRC_DIR, DIST_DIR }) => {
|
||||
app: `${SRC_DIR}/index.js`,
|
||||
},
|
||||
optimization: {
|
||||
splitChunks: {
|
||||
// include all types of chunks
|
||||
chunks: 'all',
|
||||
},
|
||||
// splitChunks: {
|
||||
// // include all types of chunks
|
||||
// chunks: 'all',
|
||||
// },
|
||||
//runtimeChunk: 'single',
|
||||
minimize: isProdBuild,
|
||||
sideEffects: true,
|
||||
},
|
||||
output: {
|
||||
clean: true,
|
||||
// clean: true,
|
||||
publicPath: '/',
|
||||
},
|
||||
context: SRC_DIR,
|
||||
@ -98,6 +101,8 @@ module.exports = (env, argv, { SRC_DIR, DIST_DIR }) => {
|
||||
path.resolve(__dirname, '../node_modules'),
|
||||
// Hoisted Yarn Workspace Modules
|
||||
path.resolve(__dirname, '../../../node_modules'),
|
||||
path.resolve(__dirname, '../platform/viewer/node_modules'),
|
||||
path.resolve(__dirname, '../platform/ui/node_modules'),
|
||||
SRC_DIR,
|
||||
],
|
||||
// Attempt to resolve these extensions in order.
|
||||
@ -131,15 +136,6 @@ module.exports = (env, argv, { SRC_DIR, DIST_DIR }) => {
|
||||
}),
|
||||
// Uncomment to generate bundle analyzer
|
||||
// new BundleAnalyzerPlugin(),
|
||||
new CopyPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from:
|
||||
'../../../node_modules/cornerstone-wado-image-loader/dist/dynamic-import',
|
||||
to: DIST_DIR,
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import { IWebApiDataSource } from '@ohif/core';
|
||||
*
|
||||
*/
|
||||
export default {
|
||||
id: 'org.ohif.*',
|
||||
id: '@ohif/extension-*',
|
||||
|
||||
/**
|
||||
* LIFECYCLE HOOKS
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
const path = require('path');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.commonjs.js');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.base.js');
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
const webpack = require('webpack');
|
||||
const merge = require('webpack-merge');
|
||||
const { merge } = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.commonjs.js');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.base.js');
|
||||
const pkg = require('./../package.json');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
|
||||
const ROOT_DIR = path.join(__dirname, './..');
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
@ -39,6 +40,10 @@ module.exports = (env, argv) => {
|
||||
new webpack.optimize.LimitChunkCountPlugin({
|
||||
maxChunks: 1,
|
||||
}),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: './dist/[name].css',
|
||||
chunkFilename: './dist/[id].css',
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
"main": "dist/index.umd.js",
|
||||
"module": "src/index.js",
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"node": ">=14",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1.16.0"
|
||||
},
|
||||
@ -16,6 +16,9 @@
|
||||
"dist",
|
||||
"README.md"
|
||||
],
|
||||
"keywords": [
|
||||
"ohif-extension"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
@ -29,8 +32,8 @@
|
||||
"test:unit:ci": "jest --ci --runInBand --collectCoverage"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^0.50.0",
|
||||
"@ohif/ui": "^0.50.0",
|
||||
"@ohif/core": "^3.0.0",
|
||||
"@ohif/ui": "^2.0.0",
|
||||
"cornerstone-core": "2.6.0",
|
||||
"cornerstone-math": "0.1.9",
|
||||
"cornerstone-tools": "6.0.2",
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
const id = 'org.ohif.cornerstone';
|
||||
import packageJson from '../package.json';
|
||||
|
||||
export default id;
|
||||
const id = packageJson.name;
|
||||
|
||||
export { id };
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
import init from './init.js';
|
||||
import commandsModule from './commandsModule.js';
|
||||
import CornerstoneViewportDownloadForm from './CornerstoneViewportDownloadForm';
|
||||
import { id } from './id.js';
|
||||
// import CornerstoneViewportDownloadForm from './CornerstoneViewportDownloadForm';
|
||||
|
||||
const Component = React.lazy(() => {
|
||||
return import(/* webpackPrefetch: true */ './OHIFCornerstoneViewport');
|
||||
@ -22,8 +23,7 @@ export default {
|
||||
/**
|
||||
* Only required property. Should be a unique value across all extensions.
|
||||
*/
|
||||
id: 'org.ohif.cornerstone',
|
||||
|
||||
id,
|
||||
/**
|
||||
*
|
||||
*
|
||||
@ -59,5 +59,3 @@ export default {
|
||||
return commandsModule({ servicesManager, commandsManager });
|
||||
},
|
||||
};
|
||||
|
||||
export { CornerstoneViewportDownloadForm };
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
const path = require('path');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.commonjs.js');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.base.js');
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
const webpack = require('webpack');
|
||||
const merge = require('webpack-merge');
|
||||
const { merge } = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.commonjs.js');
|
||||
const pkg = require('./../package.json');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.base.js');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
|
||||
const ROOT_DIR = path.join(__dirname, './..');
|
||||
const pkg = require('./../package.json');
|
||||
const ROOT_DIR = path.join(__dirname, './../');
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
|
||||
@ -30,7 +31,7 @@ module.exports = (env, argv) => {
|
||||
},
|
||||
output: {
|
||||
path: ROOT_DIR,
|
||||
library: 'OHIFExtDefault',
|
||||
library: 'OHIFExtCornerstone',
|
||||
libraryTarget: 'umd',
|
||||
libraryExport: 'default',
|
||||
filename: pkg.main,
|
||||
@ -39,6 +40,10 @@ module.exports = (env, argv) => {
|
||||
new webpack.optimize.LimitChunkCountPlugin({
|
||||
maxChunks: 1,
|
||||
}),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: './dist/[name].css',
|
||||
chunkFilename: './dist/[id].css',
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-default",
|
||||
"version": "1.0.1",
|
||||
"version": "3.0.0",
|
||||
"description": "Common/default features and functionality for basic image viewing",
|
||||
"author": "OHIF Core Team",
|
||||
"license": "MIT",
|
||||
@ -11,7 +11,7 @@
|
||||
"access": "public"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"node": ">=14",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1.18.0"
|
||||
},
|
||||
@ -19,6 +19,9 @@
|
||||
"dist",
|
||||
"README.md"
|
||||
],
|
||||
"keywords": [
|
||||
"ohif-extension"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --debug --output-pathinfo",
|
||||
"dev:dicom-pdf": "yarn run dev",
|
||||
@ -27,16 +30,14 @@
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^0.50.0",
|
||||
"@ohif/i18n": "^0.52.8",
|
||||
"@ohif/core": "^3.0.0",
|
||||
"@ohif/i18n": "^1.0.0",
|
||||
"dcmjs": "0.16.1",
|
||||
"dicomweb-client": "^0.6.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-i18next": "^10.11.0",
|
||||
"react-router": "next",
|
||||
"react-router-dom": "next",
|
||||
"webpack": "^5.50.0",
|
||||
"webpack-merge": "^5.7.3"
|
||||
},
|
||||
|
||||
@ -165,18 +165,22 @@ function PanelStudyBrowser({
|
||||
);
|
||||
const updatedExpandedStudyInstanceUIDs = shouldCollapseStudy
|
||||
? // eslint-disable-next-line prettier/prettier
|
||||
[
|
||||
...expandedStudyInstanceUIDs.filter(
|
||||
stdyUid => stdyUid !== StudyInstanceUID
|
||||
),
|
||||
]
|
||||
[
|
||||
...expandedStudyInstanceUIDs.filter(
|
||||
stdyUid => stdyUid !== StudyInstanceUID
|
||||
),
|
||||
]
|
||||
: [...expandedStudyInstanceUIDs, StudyInstanceUID];
|
||||
|
||||
setExpandedStudyInstanceUIDs(updatedExpandedStudyInstanceUIDs);
|
||||
|
||||
if (!shouldCollapseStudy) {
|
||||
const madeInClient = true
|
||||
requestDisplaySetCreationForStudy(DisplaySetService, StudyInstanceUID, madeInClient);
|
||||
const madeInClient = true;
|
||||
requestDisplaySetCreationForStudy(
|
||||
DisplaySetService,
|
||||
StudyInstanceUID,
|
||||
madeInClient
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
SidePanel,
|
||||
@ -12,12 +13,10 @@ import {
|
||||
|
||||
import i18n from '@ohif/i18n';
|
||||
import { hotkeys } from '@ohif/core';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useAppConfig } from '@state';
|
||||
|
||||
const { availableLanguages, defaultLanguage, currentLanguage } = i18n;
|
||||
|
||||
import { useAppConfig } from '@state';
|
||||
|
||||
function Toolbar({ servicesManager }) {
|
||||
const { ToolBarService } = servicesManager.services;
|
||||
const [toolbarButtons, setToolbarButtons] = useState([]);
|
||||
@ -94,7 +93,7 @@ function ViewerLayout({
|
||||
const navigate = useNavigate();
|
||||
|
||||
const onClickReturnButton = () => {
|
||||
navigate('/')
|
||||
navigate('/');
|
||||
};
|
||||
|
||||
const { t } = useTranslation();
|
||||
@ -108,7 +107,12 @@ function ViewerLayout({
|
||||
{
|
||||
title: t('Header:About'),
|
||||
icon: 'info',
|
||||
onClick: () => show({ content: AboutModal, title: 'About OHIF Viewer', contentProps: { versionNumber, buildNumber } }),
|
||||
onClick: () =>
|
||||
show({
|
||||
content: AboutModal,
|
||||
title: 'About OHIF Viewer',
|
||||
contentProps: { versionNumber, buildNumber },
|
||||
}),
|
||||
},
|
||||
{
|
||||
title: t('Header:Preferences'),
|
||||
@ -136,7 +140,7 @@ function ViewerLayout({
|
||||
hide();
|
||||
},
|
||||
onReset: () => hotkeysManager.restoreDefaultBindings(),
|
||||
hotkeysModule: hotkeys
|
||||
hotkeysModule: hotkeys,
|
||||
},
|
||||
}),
|
||||
},
|
||||
@ -185,7 +189,11 @@ function ViewerLayout({
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Header menuOptions={menuOptions} onClickReturnButton={onClickReturnButton} WhiteLabeling={appConfig.whiteLabeling} >
|
||||
<Header
|
||||
menuOptions={menuOptions}
|
||||
onClickReturnButton={onClickReturnButton}
|
||||
WhiteLabeling={appConfig.whiteLabeling}
|
||||
>
|
||||
<ErrorBoundary context="Primary Toolbar">
|
||||
<div className="relative flex justify-center">
|
||||
<Toolbar servicesManager={servicesManager} />
|
||||
@ -193,7 +201,7 @@ function ViewerLayout({
|
||||
</ErrorBoundary>
|
||||
</Header>
|
||||
<div
|
||||
className="flex flex-row flex-nowrap items-stretch w-full overflow-hidden"
|
||||
className="flex flex-row items-stretch w-full overflow-hidden flex-nowrap"
|
||||
style={{ height: 'calc(100vh - 52px' }}
|
||||
>
|
||||
{/* LEFT SIDEPANELS */}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { isImage } from '@ohif/core/src/utils/isImage';
|
||||
import ImageSet from '@ohif/core/src/classes/ImageSet';
|
||||
import isDisplaySetReconstructable from '@ohif/core/src/utils/isDisplaySetReconstructable';
|
||||
import id from './id';
|
||||
import { id } from './id';
|
||||
|
||||
const sopClassHandlerName = 'stack';
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
const id = 'org.ohif.default';
|
||||
import packageJson from '../package.json';
|
||||
|
||||
export default id;
|
||||
const id = packageJson.name;
|
||||
|
||||
export { id };
|
||||
|
||||
@ -5,7 +5,7 @@ import getSopClassHandlerModule from './getSopClassHandlerModule.js';
|
||||
import getHangingProtocolModule from './getHangingProtocolModule.js';
|
||||
import getToolbarModule from './getToolbarModule.js';
|
||||
import commandsModule from './commandsModule';
|
||||
import id from './id';
|
||||
import { id } from './id.js';
|
||||
|
||||
export default {
|
||||
/**
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
"main": "dist/index.umd.js",
|
||||
"module": "src/index.js",
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"node": ">=14",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1.16.0"
|
||||
},
|
||||
@ -29,8 +29,8 @@
|
||||
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^0.50.0",
|
||||
"@ohif/ui": "^0.50.0",
|
||||
"@ohif/core": "^3.0.0",
|
||||
"@ohif/ui": "^2.0.0",
|
||||
"cornerstone-core": "^2.6.0",
|
||||
"cornerstone-math": "^0.1.9",
|
||||
"cornerstone-tools": "6.0.2",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Name, SOPClassHandlerId } from './id';
|
||||
import { SOPClassHandlerId } from './id';
|
||||
import { utils, classes } from '@ohif/core';
|
||||
|
||||
const { ImageSet } = classes;
|
||||
@ -9,49 +9,67 @@ const SOP_CLASS_UIDS = {
|
||||
|
||||
const sopClassUids = Object.values(SOP_CLASS_UIDS);
|
||||
|
||||
|
||||
const _getDisplaySetsFromSeries = (instances, servicesManager, extensionManager) => {
|
||||
const _getDisplaySetsFromSeries = (
|
||||
instances,
|
||||
servicesManager,
|
||||
extensionManager
|
||||
) => {
|
||||
const dataSource = extensionManager.getActiveDataSource()[0];
|
||||
return instances
|
||||
.map(instance => {
|
||||
const { Modality, SOPInstanceUID, EncapsulatedDocument } = instance;
|
||||
const { SeriesDescription = "PDF", MIMETypeOfEncapsulatedDocument, } = instance;
|
||||
const { SeriesNumber, SeriesDate, SeriesInstanceUID, StudyInstanceUID, } = instance;
|
||||
const pdfUrl = dataSource.retrieve.directURL({
|
||||
instance,
|
||||
tag: 'EncapsulatedDocument',
|
||||
defaultType: MIMETypeOfEncapsulatedDocument || "application/pdf",
|
||||
singlepart: "pdf",
|
||||
});
|
||||
|
||||
const displaySet = {
|
||||
//plugin: id,
|
||||
Modality,
|
||||
displaySetInstanceUID: utils.guid(),
|
||||
SeriesDescription,
|
||||
SeriesNumber,
|
||||
SeriesDate,
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
StudyInstanceUID,
|
||||
SOPClassHandlerId,
|
||||
referencedImages: null,
|
||||
measurements: null,
|
||||
pdfUrl,
|
||||
others: [instance],
|
||||
thumbnailSrc: dataSource.retrieve.directURL({ instance, defaultPath: "/thumbnail", defaultType: "image/jpeg", tag: "Absent" }),
|
||||
isDerivedDisplaySet: true,
|
||||
isLoaded: false,
|
||||
sopClassUids,
|
||||
numImageFrames: 0,
|
||||
numInstances: 1,
|
||||
instance,
|
||||
};
|
||||
return displaySet;
|
||||
return instances.map(instance => {
|
||||
const { Modality, SOPInstanceUID, EncapsulatedDocument } = instance;
|
||||
const {
|
||||
SeriesDescription = 'PDF',
|
||||
MIMETypeOfEncapsulatedDocument,
|
||||
} = instance;
|
||||
const {
|
||||
SeriesNumber,
|
||||
SeriesDate,
|
||||
SeriesInstanceUID,
|
||||
StudyInstanceUID,
|
||||
} = instance;
|
||||
const pdfUrl = dataSource.retrieve.directURL({
|
||||
instance,
|
||||
tag: 'EncapsulatedDocument',
|
||||
defaultType: MIMETypeOfEncapsulatedDocument || 'application/pdf',
|
||||
singlepart: 'pdf',
|
||||
});
|
||||
|
||||
const displaySet = {
|
||||
//plugin: id,
|
||||
Modality,
|
||||
displaySetInstanceUID: utils.guid(),
|
||||
SeriesDescription,
|
||||
SeriesNumber,
|
||||
SeriesDate,
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
StudyInstanceUID,
|
||||
SOPClassHandlerId,
|
||||
referencedImages: null,
|
||||
measurements: null,
|
||||
pdfUrl,
|
||||
others: [instance],
|
||||
thumbnailSrc: dataSource.retrieve.directURL({
|
||||
instance,
|
||||
defaultPath: '/thumbnail',
|
||||
defaultType: 'image/jpeg',
|
||||
tag: 'Absent',
|
||||
}),
|
||||
isDerivedDisplaySet: true,
|
||||
isLoaded: false,
|
||||
sopClassUids,
|
||||
numImageFrames: 0,
|
||||
numInstances: 1,
|
||||
instance,
|
||||
};
|
||||
return displaySet;
|
||||
});
|
||||
};
|
||||
|
||||
export default function getSopClassHandlerModule({ servicesManager, extensionManager }) {
|
||||
export default function getSopClassHandlerModule({
|
||||
servicesManager,
|
||||
extensionManager,
|
||||
}) {
|
||||
const getDisplaySetsFromSeries = instances => {
|
||||
return _getDisplaySetsFromSeries(
|
||||
instances,
|
||||
@ -62,7 +80,7 @@ export default function getSopClassHandlerModule({ servicesManager, extensionMan
|
||||
|
||||
return [
|
||||
{
|
||||
name: Name,
|
||||
name: 'dicom-pdf',
|
||||
sopClassUids,
|
||||
getDisplaySetsFromSeries,
|
||||
},
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
const Name = 'dicom-pdf';
|
||||
const id = `org.ohif.${Name}`;
|
||||
import packageJson from '../package.json';
|
||||
|
||||
export default id;
|
||||
const id = packageJson.name;
|
||||
const SOPClassHandlerId = `${id}.sopClassHandlerModule.dicom-pdf`;
|
||||
|
||||
const SOPClassHandlerId = `${id}.sopClassHandlerModule.${Name}`;
|
||||
|
||||
export { Name, SOPClassHandlerId, };
|
||||
export { id, SOPClassHandlerId };
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import getSopClassHandlerModule from './getSopClassHandlerModule';
|
||||
import id from './id.js';
|
||||
import { id } from './id.js';
|
||||
|
||||
const Component = React.lazy(() => {
|
||||
return import(
|
||||
@ -24,23 +24,6 @@ export default {
|
||||
* Only required property. Should be a unique value across all extensions.
|
||||
*/
|
||||
id,
|
||||
dependencies: [
|
||||
// TODO -> This isn't used anywhere yet, but we do have a hard dependency, and need to check for these in the future.
|
||||
// OHIF-229
|
||||
{
|
||||
id: 'org.ohif.cornerstone',
|
||||
version: '3.0.0',
|
||||
},
|
||||
{
|
||||
id: 'org.ohif.measurement-tracking',
|
||||
version: '^0.0.1',
|
||||
},
|
||||
],
|
||||
|
||||
preRegistration({ servicesManager, configuration = {} }) {
|
||||
// No-op for now
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
@ -58,7 +41,9 @@ export default {
|
||||
);
|
||||
};
|
||||
|
||||
return [{ name: 'dicom-pdf', component: ExtendedOHIFCornerstonePdfViewport }];
|
||||
return [
|
||||
{ name: 'dicom-pdf', component: ExtendedOHIFCornerstonePdfViewport },
|
||||
];
|
||||
},
|
||||
getCommandsModule({ servicesManager }) {
|
||||
return {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
const path = require('path');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.commonjs.js');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.base.js');
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
const webpack = require('webpack');
|
||||
const merge = require('webpack-merge');
|
||||
const { merge } = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.commonjs.js');
|
||||
const pkg = require('./../package.json');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
|
||||
const ROOT_DIR = path.join(__dirname, './..');
|
||||
const pkg = require('./../package.json');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.base.js');
|
||||
|
||||
const ROOT_DIR = path.join(__dirname, './../');
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
|
||||
@ -30,7 +32,7 @@ module.exports = (env, argv) => {
|
||||
},
|
||||
output: {
|
||||
path: ROOT_DIR,
|
||||
library: 'OHIFExtDICOMSR',
|
||||
library: 'OHIFExtCornerstone',
|
||||
libraryTarget: 'umd',
|
||||
libraryExport: 'default',
|
||||
filename: pkg.main,
|
||||
@ -39,6 +41,10 @@ module.exports = (env, argv) => {
|
||||
new webpack.optimize.LimitChunkCountPlugin({
|
||||
maxChunks: 1,
|
||||
}),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: './dist/[name].css',
|
||||
chunkFilename: './dist/[id].css',
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-sr",
|
||||
"version": "0.0.1",
|
||||
"version": "3.0.0",
|
||||
"description": "OHIF extension for an SR Cornerstone Viewport",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@ -8,7 +8,7 @@
|
||||
"main": "dist/index.umd.js",
|
||||
"module": "src/index.js",
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"node": ">=14",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1.16.0"
|
||||
},
|
||||
@ -19,6 +19,9 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"keywords": [
|
||||
"ohif-extension"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --debug --output-pathinfo",
|
||||
"dev:cornerstone": "yarn run dev",
|
||||
@ -29,8 +32,8 @@
|
||||
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^0.50.0",
|
||||
"@ohif/ui": "^0.50.0",
|
||||
"@ohif/core": "^3.0.0",
|
||||
"@ohif/ui": "^2.0.0",
|
||||
"cornerstone-core": "^2.6.0",
|
||||
"cornerstone-math": "^0.1.9",
|
||||
"cornerstone-tools": "6.0.2",
|
||||
@ -40,7 +43,9 @@
|
||||
"hammerjs": "^2.0.8",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^17.0.2",
|
||||
"react-cornerstone-viewport": "4.1.2"
|
||||
"react-cornerstone-viewport": "4.1.2",
|
||||
"@ohif/extension-cornerstone": "^3.0.0",
|
||||
"@ohif/extension-measurement-tracking": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.16.3",
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
const id = 'org.ohif.dicom-sr';
|
||||
import packageJson from '../package.json';
|
||||
|
||||
export default id;
|
||||
const id = packageJson.name;
|
||||
|
||||
const SOPClassHandlerName = 'dicom-sr';
|
||||
const SOPClassHandlerId = `${id}.sopClassHandlerModule.${SOPClassHandlerName}`;
|
||||
export { SOPClassHandlerName, SOPClassHandlerId };
|
||||
|
||||
export { SOPClassHandlerName, SOPClassHandlerId, id };
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import getSopClassHandlerModule from './getSopClassHandlerModule';
|
||||
import onModeEnter from './onModeEnter';
|
||||
import id from './id.js';
|
||||
import init from './init';
|
||||
import { id } from './id.js';
|
||||
|
||||
const Component = React.lazy(() => {
|
||||
return import(
|
||||
@ -26,18 +26,6 @@ export default {
|
||||
* Only required property. Should be a unique value across all extensions.
|
||||
*/
|
||||
id,
|
||||
dependencies: [
|
||||
// TODO -> This isn't used anywhere yet, but we do have a hard dependency, and need to check for these in the future.
|
||||
// OHIF-229
|
||||
{
|
||||
id: 'org.ohif.cornerstone',
|
||||
version: '3.0.0',
|
||||
},
|
||||
{
|
||||
id: 'org.ohif.measurement-tracking',
|
||||
version: '^0.0.1',
|
||||
},
|
||||
],
|
||||
|
||||
preRegistration({ servicesManager, configuration = {} }) {
|
||||
init({ servicesManager, configuration });
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import cornerstoneTools from 'cornerstone-tools';
|
||||
import dicomSRModule from './tools/modules/dicomSRModule';
|
||||
import id from './id';
|
||||
import { id } from './id';
|
||||
|
||||
import TOOL_NAMES from './constants/toolNames';
|
||||
|
||||
@ -14,9 +14,9 @@ const defaultConfig = {
|
||||
* @param {object} configuration
|
||||
*/
|
||||
export default function init({ configuration = {} }) {
|
||||
const conifg = Object.assign({}, defaultConfig, configuration);
|
||||
const config = Object.assign({}, defaultConfig, configuration);
|
||||
|
||||
TOOL_NAMES.DICOM_SR_DISPLAY_TOOL = conifg.TOOL_NAMES.DICOM_SR_DISPLAY_TOOL;
|
||||
TOOL_NAMES.DICOM_SR_DISPLAY_TOOL = config.TOOL_NAMES.DICOM_SR_DISPLAY_TOOL;
|
||||
|
||||
cornerstoneTools.register('module', id, dicomSRModule);
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import { pixelToCanvas } from 'cornerstone-core';
|
||||
|
||||
import TOOL_NAMES from '../constants/toolNames';
|
||||
import SCOORD_TYPES from '../constants/scoordTypes';
|
||||
import id from '../id';
|
||||
import { id } from '../id';
|
||||
|
||||
// Cornerstone 3rd party dev kit imports
|
||||
const draw = importInternal('drawing/draw');
|
||||
|
||||
@ -14,7 +14,7 @@ import {
|
||||
} from '@ohif/ui';
|
||||
import TOOL_NAMES from './../constants/toolNames';
|
||||
import { adapters } from 'dcmjs';
|
||||
import id from './../id';
|
||||
import { id } from './../id';
|
||||
|
||||
const { formatDate } = utils;
|
||||
const scrollToIndex = cornerstoneTools.importInternal('util/scrollToIndex');
|
||||
@ -23,7 +23,8 @@ const globalImageIdSpecificToolStateManager =
|
||||
|
||||
const { StackManager, guid } = OHIF.utils;
|
||||
|
||||
const MEASUREMENT_TRACKING_EXTENSION_ID = 'org.ohif.measurement-tracking';
|
||||
const MEASUREMENT_TRACKING_EXTENSION_ID =
|
||||
'@ohif/extension-measurement-tracking';
|
||||
|
||||
function OHIFCornerstoneSRViewport({
|
||||
children,
|
||||
@ -81,7 +82,7 @@ function OHIFCornerstoneSRViewport({
|
||||
)
|
||||
) {
|
||||
const contextModule = extensionManager.getModuleEntry(
|
||||
'org.ohif.measurement-tracking.contextModule.TrackedMeasurementsContext'
|
||||
'@ohif/extension-measurement-tracking.contextModule.TrackedMeasurementsContext'
|
||||
);
|
||||
|
||||
const useTrackedMeasurements = () => useContext(contextModule.context);
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
"main": "dist/index.umd.js",
|
||||
"module": "src/index.js",
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"node": ">=14",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1.16.0"
|
||||
},
|
||||
@ -30,7 +30,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^0.50.0",
|
||||
"@ohif/ui": "^0.50.0",
|
||||
"@ohif/ui": "^2.0.0",
|
||||
"cornerstone-core": "^2.6.0",
|
||||
"cornerstone-math": "^0.1.9",
|
||||
"cornerstone-tools": "6.0.2",
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Name, SOPClassHandlerId } from './id';
|
||||
import { utils, } from '@ohif/core';
|
||||
import { SOPClassHandlerId } from './id';
|
||||
import { utils } from '@ohif/core';
|
||||
|
||||
const SOP_CLASS_UIDS = {
|
||||
VIDEO_MICROSCOPIC_IMAGE_STORAGE: '1.2.840.10008.5.1.4.1.1.77.1.2.1',
|
||||
@ -25,17 +25,33 @@ const SupportedTransferSyntaxes = {
|
||||
|
||||
const supportedTransferSyntaxUIDs = Object.values(SupportedTransferSyntaxes);
|
||||
|
||||
const _getDisplaySetsFromSeries = (instances, servicesManager, extensionManager) => {
|
||||
const _getDisplaySetsFromSeries = (
|
||||
instances,
|
||||
servicesManager,
|
||||
extensionManager
|
||||
) => {
|
||||
const dataSource = extensionManager.getActiveDataSource()[0];
|
||||
return instances
|
||||
.filter(metadata => {
|
||||
const tsuid = metadata.AvailableTransferSyntaxUID ||
|
||||
metadata.TransferSyntaxUID || metadata['00083002'];
|
||||
const tsuid =
|
||||
metadata.AvailableTransferSyntaxUID ||
|
||||
metadata.TransferSyntaxUID ||
|
||||
metadata['00083002'];
|
||||
return supportedTransferSyntaxUIDs.includes(tsuid);
|
||||
})
|
||||
.map(instance => {
|
||||
const { Modality, SOPInstanceUID, SeriesDescription = "VIDEO" } = instance;
|
||||
const { SeriesNumber, SeriesDate, SeriesInstanceUID, StudyInstanceUID, NumberOfFrames } = instance;
|
||||
const {
|
||||
Modality,
|
||||
SOPInstanceUID,
|
||||
SeriesDescription = 'VIDEO',
|
||||
} = instance;
|
||||
const {
|
||||
SeriesNumber,
|
||||
SeriesDate,
|
||||
SeriesInstanceUID,
|
||||
StudyInstanceUID,
|
||||
NumberOfFrames,
|
||||
} = instance;
|
||||
const displaySet = {
|
||||
//plugin: id,
|
||||
Modality,
|
||||
@ -49,9 +65,18 @@ const _getDisplaySetsFromSeries = (instances, servicesManager, extensionManager)
|
||||
SOPClassHandlerId,
|
||||
referencedImages: null,
|
||||
measurements: null,
|
||||
videoUrl: dataSource.retrieve.directURL({ instance, singlepart: "video", tag: "PixelData", }),
|
||||
videoUrl: dataSource.retrieve.directURL({
|
||||
instance,
|
||||
singlepart: 'video',
|
||||
tag: 'PixelData',
|
||||
}),
|
||||
others: [instance],
|
||||
thumbnailSrc: dataSource.retrieve.directURL({ instance, defaultPath: "/thumbnail", defaultType: "image/jpeg", tag: "Absent" }),
|
||||
thumbnailSrc: dataSource.retrieve.directURL({
|
||||
instance,
|
||||
defaultPath: '/thumbnail',
|
||||
defaultType: 'image/jpeg',
|
||||
tag: 'Absent',
|
||||
}),
|
||||
isDerivedDisplaySet: true,
|
||||
isLoaded: false,
|
||||
sopClassUids,
|
||||
@ -62,7 +87,10 @@ const _getDisplaySetsFromSeries = (instances, servicesManager, extensionManager)
|
||||
});
|
||||
};
|
||||
|
||||
export default function getSopClassHandlerModule({ servicesManager, extensionManager }) {
|
||||
export default function getSopClassHandlerModule({
|
||||
servicesManager,
|
||||
extensionManager,
|
||||
}) {
|
||||
const getDisplaySetsFromSeries = instances => {
|
||||
return _getDisplaySetsFromSeries(
|
||||
instances,
|
||||
@ -73,7 +101,7 @@ export default function getSopClassHandlerModule({ servicesManager, extensionMan
|
||||
|
||||
return [
|
||||
{
|
||||
name: Name,
|
||||
name: 'dicom-video',
|
||||
sopClassUids,
|
||||
getDisplaySetsFromSeries,
|
||||
},
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
const Name = 'dicom-video';
|
||||
const id = `org.ohif.${Name}`;
|
||||
import packageJson from '../package.json';
|
||||
|
||||
export default id;
|
||||
const id = packageJson.name;
|
||||
const SOPClassHandlerId = `${id}.sopClassHandlerModule.dicom-video`;
|
||||
|
||||
const SOPClassHandlerId = `${id}.sopClassHandlerModule.${Name}`;
|
||||
|
||||
export { Name, SOPClassHandlerId, };
|
||||
export { SOPClassHandlerId, id };
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import getSopClassHandlerModule from './getSopClassHandlerModule';
|
||||
import id from './id.js';
|
||||
import { id } from './id';
|
||||
|
||||
const Component = React.lazy(() => {
|
||||
return import(
|
||||
@ -24,22 +24,6 @@ export default {
|
||||
* Only required property. Should be a unique value across all extensions.
|
||||
*/
|
||||
id,
|
||||
dependencies: [
|
||||
// TODO -> This isn't used anywhere yet, but we do have a hard dependency, and need to check for these in the future.
|
||||
// OHIF-229
|
||||
{
|
||||
id: 'org.ohif.cornerstone',
|
||||
version: '3.0.0',
|
||||
},
|
||||
{
|
||||
id: 'org.ohif.measurement-tracking',
|
||||
version: '^0.0.1',
|
||||
},
|
||||
],
|
||||
|
||||
preRegistration({ servicesManager, configuration = {} }) {
|
||||
// No-op for now
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
@ -58,7 +42,9 @@ export default {
|
||||
);
|
||||
};
|
||||
|
||||
return [{ name: 'dicom-video', component: ExtendedOHIFCornerstoneVideoViewport }];
|
||||
return [
|
||||
{ name: 'dicom-video', component: ExtendedOHIFCornerstoneVideoViewport },
|
||||
];
|
||||
},
|
||||
getCommandsModule({ servicesManager }) {
|
||||
return {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
const path = require('path');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.commonjs.js');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.base.js');
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
const webpack = require('webpack');
|
||||
const merge = require('webpack-merge');
|
||||
const { merge } = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.commonjs.js');
|
||||
const pkg = require('./../package.json');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
|
||||
const ROOT_DIR = path.join(__dirname, './..');
|
||||
const pkg = require('./../package.json');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.base.js');
|
||||
|
||||
const ROOT_DIR = path.join(__dirname, './../');
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
|
||||
@ -30,7 +32,7 @@ module.exports = (env, argv) => {
|
||||
},
|
||||
output: {
|
||||
path: ROOT_DIR,
|
||||
library: 'OHIFExtDefault',
|
||||
library: 'OHIFExtCornerstone',
|
||||
libraryTarget: 'umd',
|
||||
libraryExport: 'default',
|
||||
filename: pkg.main,
|
||||
@ -39,6 +41,10 @@ module.exports = (env, argv) => {
|
||||
new webpack.optimize.LimitChunkCountPlugin({
|
||||
maxChunks: 1,
|
||||
}),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: './dist/[name].css',
|
||||
chunkFilename: './dist/[id].css',
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/extension-measurement-tracking",
|
||||
"version": "0.0.1",
|
||||
"version": "3.0.0",
|
||||
"description": "Tracking features and functionality for basic image viewing",
|
||||
"author": "OHIF Core Team",
|
||||
"license": "MIT",
|
||||
@ -11,7 +11,7 @@
|
||||
"access": "public"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"node": ">=14",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1.18.0"
|
||||
},
|
||||
@ -19,6 +19,9 @@
|
||||
"dist",
|
||||
"README.md"
|
||||
],
|
||||
"keywords": [
|
||||
"ohif-extension"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --debug --output-pathinfo",
|
||||
"dev:dicom-pdf": "yarn run dev",
|
||||
@ -27,21 +30,22 @@
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^0.50.0",
|
||||
"@ohif/core": "^3.0.0",
|
||||
"classnames": "^2.2.6",
|
||||
"cornerstone-core": "^2.6.0",
|
||||
"cornerstone-tools": "6.0.2",
|
||||
"dcmjs": "0.16.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.13.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-cornerstone-viewport": "^4.1.2",
|
||||
"react-dom": "^16.13.1",
|
||||
"webpack": "^5.50.0",
|
||||
"webpack-merge": "^5.7.3"
|
||||
"webpack-merge": "^5.7.3",
|
||||
"@ohif/ui": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.16.3",
|
||||
"@ohif/ui": "^1.8.2",
|
||||
"@ohif/ui": "^2.0.0",
|
||||
"@xstate/react": "^0.8.1",
|
||||
"xstate": "^4.10.0"
|
||||
}
|
||||
|
||||
@ -17,7 +17,8 @@ const TrackedMeasurementsContext = React.createContext();
|
||||
TrackedMeasurementsContext.displayName = 'TrackedMeasurementsContext';
|
||||
const useTrackedMeasurements = () => useContext(TrackedMeasurementsContext);
|
||||
|
||||
const SR_SOPCLASSHANDLERID = "org.ohif.dicom-sr.sopClassHandlerModule.dicom-sr";
|
||||
const SR_SOPCLASSHANDLERID =
|
||||
'@ohif/extension-dicom-sr.sopClassHandlerModule.dicom-sr';
|
||||
|
||||
/**
|
||||
*
|
||||
@ -33,7 +34,10 @@ function TrackedMeasurementsContextProvider(
|
||||
const machineOptions = Object.assign({}, defaultOptions);
|
||||
machineOptions.actions = Object.assign({}, machineOptions.actions, {
|
||||
jumpToFirstMeasurementInActiveViewport: (ctx, evt) => {
|
||||
const { DisplaySetService, MeasurementService } = servicesManager.services;
|
||||
const {
|
||||
DisplaySetService,
|
||||
MeasurementService,
|
||||
} = servicesManager.services;
|
||||
const { trackedStudy, trackedSeries } = ctx;
|
||||
const measurements = MeasurementService.getMeasurements();
|
||||
const trackedMeasurements = measurements.filter(
|
||||
@ -44,7 +48,10 @@ function TrackedMeasurementsContextProvider(
|
||||
|
||||
const id = trackedMeasurements[0].id;
|
||||
|
||||
MeasurementService.jumpToMeasurement(viewportGrid.activeViewportIndex, id);
|
||||
MeasurementService.jumpToMeasurement(
|
||||
viewportGrid.activeViewportIndex,
|
||||
id
|
||||
);
|
||||
},
|
||||
showStructuredReportDisplaySetInActiveViewport: (ctx, evt) => {
|
||||
if (evt.data.createdDisplaySetInstanceUIDs.length > 0) {
|
||||
@ -98,7 +105,7 @@ function TrackedMeasurementsContextProvider(
|
||||
}),
|
||||
promptHydrateStructuredReport: promptHydrateStructuredReport.bind(null, {
|
||||
servicesManager,
|
||||
extensionManager
|
||||
extensionManager,
|
||||
}),
|
||||
});
|
||||
|
||||
@ -149,9 +156,11 @@ function TrackedMeasurementsContextProvider(
|
||||
// The issue here is that this handler in TrackedMeasurementsContext
|
||||
// ends up occurring before the Viewport is created, so the displaySet
|
||||
// is not loaded yet, and isRehydratable is undefined unless we call load().
|
||||
if (displaySet.SOPClassHandlerId === SR_SOPCLASSHANDLERID &&
|
||||
!displaySet.isLoaded &&
|
||||
displaySet.load) {
|
||||
if (
|
||||
displaySet.SOPClassHandlerId === SR_SOPCLASSHANDLERID &&
|
||||
!displaySet.isLoaded &&
|
||||
displaySet.load
|
||||
) {
|
||||
displaySet.load();
|
||||
}
|
||||
|
||||
|
||||
5
extensions/measurement-tracking/src/id.js
Normal file
5
extensions/measurement-tracking/src/id.js
Normal file
@ -0,0 +1,5 @@
|
||||
import packageJson from '../package.json';
|
||||
|
||||
const id = packageJson.name;
|
||||
|
||||
export { id };
|
||||
@ -1,12 +1,14 @@
|
||||
import getContextModule from './getContextModule.js';
|
||||
import getPanelModule from './getPanelModule.js';
|
||||
import getViewportModule from './getViewportModule.js';
|
||||
import { id } from './id.js';
|
||||
|
||||
export default {
|
||||
/**
|
||||
* Only required property. Should be a unique value across all extensions.
|
||||
*/
|
||||
id: 'org.ohif.measurement-tracking',
|
||||
id,
|
||||
|
||||
getContextModule,
|
||||
getPanelModule,
|
||||
getViewportModule,
|
||||
|
||||
@ -228,7 +228,7 @@ function TrackedCornerstoneViewport(props) {
|
||||
|
||||
const renderViewport = () => {
|
||||
const { component: Component } = extensionManager.getModuleEntry(
|
||||
'org.ohif.cornerstone.viewportModule.cornerstone'
|
||||
'@ohif/extension-cornerstone.viewportModule.cornerstone'
|
||||
);
|
||||
return (
|
||||
<Component
|
||||
@ -257,7 +257,7 @@ function TrackedCornerstoneViewport(props) {
|
||||
isLocked: false,
|
||||
isRehydratable: false,
|
||||
studyDate: formatDate(SeriesDate), // TODO: This is series date. Is that ok?
|
||||
currentSeries: SeriesNumber, // TODO - switch entire currentSeries to be UID based or actual position based
|
||||
currentSeries: SeriesNumber, // TODO - switch entire currentSeries to be UID based or actual position based
|
||||
seriesDescription: SeriesDescription,
|
||||
modality: Modality,
|
||||
patientInformation: {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
const path = require('path');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.commonjs.js');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.base.js');
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
const webpack = require('webpack');
|
||||
const merge = require('webpack-merge');
|
||||
const { merge } = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.commonjs.js');
|
||||
const pkg = require('./../package.json');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
|
||||
const ROOT_DIR = path.join(__dirname, './..');
|
||||
const pkg = require('./../package.json');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.base.js');
|
||||
|
||||
const ROOT_DIR = path.join(__dirname, './../');
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
|
||||
const fileName = 'index.umd.js';
|
||||
module.exports = (env, argv) => {
|
||||
const commonConfig = webpackCommon(env, argv, { SRC_DIR, DIST_DIR });
|
||||
|
||||
@ -30,7 +33,7 @@ module.exports = (env, argv) => {
|
||||
},
|
||||
output: {
|
||||
path: ROOT_DIR,
|
||||
library: 'OHIFModeLongitudinal',
|
||||
library: 'OHIFExtCornerstone',
|
||||
libraryTarget: 'umd',
|
||||
libraryExport: 'default',
|
||||
filename: pkg.main,
|
||||
@ -39,6 +42,10 @@ module.exports = (env, argv) => {
|
||||
new webpack.optimize.LimitChunkCountPlugin({
|
||||
maxChunks: 1,
|
||||
}),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: './dist/[name].css',
|
||||
chunkFilename: './dist/[id].css',
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ohif/mode-longitudinal",
|
||||
"version": "0.0.1",
|
||||
"version": "3.0.0",
|
||||
"description": "Longitudinal Workflow",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
@ -8,7 +8,7 @@
|
||||
"main": "dist/index.umd.js",
|
||||
"module": "src/index.js",
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"node": ">=14",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1.16.0"
|
||||
},
|
||||
@ -19,6 +19,9 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"keywords": [
|
||||
"ohif-mode"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --debug --output-pathinfo",
|
||||
"dev:cornerstone": "yarn run dev",
|
||||
@ -29,7 +32,13 @@
|
||||
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^2.12.3"
|
||||
"@ohif/core": "^3.0.0",
|
||||
"@ohif/extension-default": "^3.0.0",
|
||||
"@ohif/extension-cornerstone": "^3.0.0",
|
||||
"@ohif/extension-dicom-sr": "^3.0.0",
|
||||
"@ohif/extension-dicom-pdf": "^3.0.1",
|
||||
"@ohif/extension-dicom-video": "^3.0.1",
|
||||
"@ohif/extension-measurement-tracking": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.16.3"
|
||||
|
||||
5
modes/longitudinal/src/id.js
Normal file
5
modes/longitudinal/src/id.js
Normal file
@ -0,0 +1,5 @@
|
||||
import packageJson from '../package.json';
|
||||
|
||||
const id = packageJson.name;
|
||||
|
||||
export { id };
|
||||
@ -1,38 +1,53 @@
|
||||
import toolbarButtons from './toolbarButtons.js';
|
||||
import { hotkeys } from '@ohif/core';
|
||||
import toolbarButtons from './toolbarButtons.js';
|
||||
import { id } from './id.js';
|
||||
|
||||
const ohif = {
|
||||
layout: 'org.ohif.default.layoutTemplateModule.viewerLayout',
|
||||
sopClassHandler: 'org.ohif.default.sopClassHandlerModule.stack',
|
||||
hangingProtocols: 'org.ohif.default.hangingProtocolModule.default',
|
||||
layout: '@ohif/extension-default.layoutTemplateModule.viewerLayout',
|
||||
sopClassHandler: '@ohif/extension-default.sopClassHandlerModule.stack',
|
||||
hangingProtocols: '@ohif/extension-default.hangingProtocolModule.default',
|
||||
};
|
||||
|
||||
const tracked = {
|
||||
measurements: 'org.ohif.measurement-tracking.panelModule.trackedMeasurements',
|
||||
thumbnailList: 'org.ohif.measurement-tracking.panelModule.seriesList',
|
||||
viewport: 'org.ohif.measurement-tracking.viewportModule.cornerstone-tracked',
|
||||
measurements:
|
||||
'@ohif/extension-measurement-tracking.panelModule.trackedMeasurements',
|
||||
thumbnailList: '@ohif/extension-measurement-tracking.panelModule.seriesList',
|
||||
viewport:
|
||||
'@ohif/extension-measurement-tracking.viewportModule.cornerstone-tracked',
|
||||
};
|
||||
|
||||
const dicomsr = {
|
||||
sopClassHandler: 'org.ohif.dicom-sr.sopClassHandlerModule.dicom-sr',
|
||||
viewport: 'org.ohif.dicom-sr.viewportModule.dicom-sr',
|
||||
sopClassHandler: '@ohif/extension-dicom-sr.sopClassHandlerModule.dicom-sr',
|
||||
viewport: '@ohif/extension-dicom-sr.viewportModule.dicom-sr',
|
||||
};
|
||||
|
||||
const dicomvideo = {
|
||||
sopClassHandler: 'org.ohif.dicom-video.sopClassHandlerModule.dicom-video',
|
||||
viewport: 'org.ohif.dicom-video.viewportModule.dicom-video',
|
||||
}
|
||||
sopClassHandler:
|
||||
'@ohif/extension-dicom-video.sopClassHandlerModule.dicom-video',
|
||||
viewport: '@ohif/extension-dicom-video.viewportModule.dicom-video',
|
||||
};
|
||||
|
||||
const dicompdf = {
|
||||
sopClassHandler: 'org.ohif.dicom-pdf.sopClassHandlerModule.dicom-pdf',
|
||||
viewport: 'org.ohif.dicom-pdf.viewportModule.dicom-pdf',
|
||||
}
|
||||
sopClassHandler: '@ohif/extension-dicom-pdf.sopClassHandlerModule.dicom-pdf',
|
||||
viewport: '@ohif/extension-dicom-pdf.viewportModule.dicom-pdf',
|
||||
};
|
||||
|
||||
export default function mode({ modeConfiguration }) {
|
||||
const extensionDependencies = {
|
||||
// Can derive the versions at least process.env.from npm_package_version
|
||||
'@ohif/extension-default': '^3.0.0',
|
||||
'@ohif/extension-cornerstone': '^3.0.0',
|
||||
'@ohif/extension-measurement-tracking': '^3.0.0',
|
||||
'@ohif/extension-dicom-sr': '^3.0.0',
|
||||
'@ohif/extension-dicom-pdf': '^3.0.1',
|
||||
'@ohif/extension-dicom-video': '^3.0.1',
|
||||
};
|
||||
|
||||
function modeFactory({ modeConfiguration }) {
|
||||
return {
|
||||
// TODO: We're using this as a route segment
|
||||
// We should not be.
|
||||
id: 'viewer',
|
||||
id,
|
||||
routeName: 'viewer',
|
||||
displayName: 'Basic Viewer',
|
||||
/**
|
||||
* Lifecycle hooks
|
||||
@ -73,7 +88,7 @@ export default function mode({ modeConfiguration }) {
|
||||
const modalities_list = modalities.split('\\');
|
||||
|
||||
// Slide Microscopy modality not supported by basic mode yet
|
||||
return !modalities_list.includes('SM')
|
||||
return !modalities_list.includes('SM');
|
||||
},
|
||||
routes: [
|
||||
{
|
||||
@ -111,26 +126,26 @@ export default function mode({ modeConfiguration }) {
|
||||
},
|
||||
},
|
||||
],
|
||||
extensions: [
|
||||
'org.ohif.default',
|
||||
'org.ohif.cornerstone',
|
||||
'org.ohif.measurement-tracking',
|
||||
'org.ohif.dicom-sr',
|
||||
'org.ohif.dicom-video',
|
||||
'org.ohif.dicom-pdf',
|
||||
],
|
||||
extensions: extensionDependencies,
|
||||
hangingProtocols: [ohif.hangingProtocols],
|
||||
// Order is important in sop class handlers when two handlers both use
|
||||
// the same sop class under different situations. In that case, the more
|
||||
// general handler needs to come last. For this case, the dicomvideo msut
|
||||
// general handler needs to come last. For this case, the dicomvideo must
|
||||
// come first to remove video transfer syntax before ohif uses images
|
||||
sopClassHandlers: [
|
||||
dicomvideo.sopClassHandler,
|
||||
ohif.sopClassHandler,
|
||||
dicompdf.sopClassHandler,
|
||||
dicomsr.sopClassHandler,],
|
||||
dicomsr.sopClassHandler,
|
||||
],
|
||||
hotkeys: [...hotkeys.defaults.hotkeyBindings],
|
||||
};
|
||||
}
|
||||
|
||||
window.longitudinalMode = mode({});
|
||||
const mode = {
|
||||
id,
|
||||
modeFactory,
|
||||
extensionDependencies,
|
||||
};
|
||||
|
||||
export default mode;
|
||||
|
||||
54
package.json
54
package.json
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "root",
|
||||
"name": "ohif-monorepo-root",
|
||||
"private": true,
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
@ -12,7 +12,7 @@
|
||||
]
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"node": ">=14",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1.16.0"
|
||||
},
|
||||
@ -22,8 +22,10 @@
|
||||
"build:dev": "lerna run build:dev --stream",
|
||||
"build:ci": "lerna run build:viewer:ci --stream",
|
||||
"build:qa": "lerna run build:viewer:qa --stream",
|
||||
"cli": "node ./platform/cli/src/index.js",
|
||||
"build:ui:deploy-preview": "lerna run build:ui:deploy-preview --stream",
|
||||
"build:demo": "lerna run build:viewer:demo --stream",
|
||||
"build:package-all": "lerna run build:package --parallel --stream",
|
||||
"dev": "lerna run dev:viewer --stream",
|
||||
"dev:project": ".scripts/dev.sh",
|
||||
"dev:orthanc": "lerna run dev:orthanc --stream",
|
||||
@ -48,7 +50,7 @@
|
||||
"lerna:cache": "./netlify-lerna-cache.sh",
|
||||
"lerna:restore": "./netlify-lerna-restore.sh",
|
||||
"lerna:version": "npx lerna version prerelease --force-publish",
|
||||
"lerna:publish": "lerna publish from-package --canary --dist-tag canary",
|
||||
"lerna:publish": "lerna publish from-package --dist-tag next",
|
||||
"link-list": "npm ls --depth=0 --link=true"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -62,18 +64,18 @@
|
||||
"react-dom": "17.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.5.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.5.0",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.5.5",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||
"@babel/plugin-transform-arrow-functions": "^7.2.0",
|
||||
"@babel/plugin-transform-regenerator": "^7.4.5",
|
||||
"@babel/plugin-transform-runtime": "^7.5.0",
|
||||
"@babel/preset-env": "^7.5.0",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"autoprefixer": "10.2.4",
|
||||
"@babel/core": "^7.17.8",
|
||||
"@babel/plugin-proposal-class-properties": "^7.16.7",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.17.3",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
|
||||
"@babel/plugin-transform-arrow-functions": "^7.16.7",
|
||||
"@babel/plugin-transform-regenerator": "^7.16.7",
|
||||
"@babel/plugin-transform-runtime": "^7.17.0",
|
||||
"@babel/preset-env": "^7.16.11",
|
||||
"@babel/preset-react": "^7.16.7",
|
||||
"autoprefixer": "10.4.4",
|
||||
"babel-eslint": "9.x",
|
||||
"babel-loader": "^8.0.6",
|
||||
"babel-loader": "^8.2.4",
|
||||
"babel-plugin-inline-react-svg": "1.1.0",
|
||||
"babel-plugin-module-resolver": "^3.2.0",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
@ -81,17 +83,17 @@
|
||||
"cross-env": "^5.2.0",
|
||||
"css-loader": "^3.2.0",
|
||||
"dotenv": "^8.1.0",
|
||||
"eslint": "6.8.0",
|
||||
"eslint-config-prettier": "^6.4.0",
|
||||
"eslint-config-react-app": "^5.2.0",
|
||||
"eslint-plugin-flowtype": "2.x",
|
||||
"eslint-plugin-import": "2.x",
|
||||
"eslint-plugin-jsx-a11y": "6.x",
|
||||
"eslint-plugin-node": "^9.1.0",
|
||||
"eslint-plugin-prettier": "^3.1.1",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"eslint-plugin-react": "7.x",
|
||||
"eslint-plugin-react-hooks": "4.2.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^7.2.0",
|
||||
"eslint-config-react-app": "^6.0.0",
|
||||
"eslint-plugin-flowtype": "^7.0.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-promise": "^5.2.0",
|
||||
"eslint-plugin-react": "^7.29.4",
|
||||
"eslint-plugin-react-hooks": "^4.4.0",
|
||||
"html-webpack-plugin": "^5.3.2",
|
||||
"husky": "^3.0.0",
|
||||
"jest": "^24.8.0",
|
||||
@ -104,7 +106,7 @@
|
||||
"postcss": "^8.3.5",
|
||||
"postcss-import": "^14.0.2",
|
||||
"postcss-loader": "^6.1.1",
|
||||
"postcss-preset-env": "^6.7.0",
|
||||
"postcss-preset-env": "^7.4.3",
|
||||
"prettier": "^1.18.2",
|
||||
"react-hot-loader": "^4.13.0",
|
||||
"serve": "^11.1.0",
|
||||
|
||||
43
platform/cli/package.json
Normal file
43
platform/cli/package.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "@ohif/cli",
|
||||
"version": "2.0.7",
|
||||
"description": "A CLI to bootstrap new OHIF extension or mode",
|
||||
"type": "module",
|
||||
"main": "src/index.js",
|
||||
"private": true,
|
||||
"bin": {
|
||||
"ohif-cli": "src/index.js"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [
|
||||
"cli",
|
||||
"ohif"
|
||||
],
|
||||
"author": "OHIF Contributors",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.13.10",
|
||||
"chalk": "^5.0.0",
|
||||
"commander": "^8.3.0",
|
||||
"axios": "^0.26.1",
|
||||
"execa": "^6.0.0",
|
||||
"gitignore": "^0.7.0",
|
||||
"inquirer": "^8.2.0",
|
||||
"listr": "^0.14.3",
|
||||
"mustache": "^4.2.0",
|
||||
"ncp": "^2.0.0",
|
||||
"node-fetch": "^3.1.1",
|
||||
"pkg-install": "^1.0.0",
|
||||
"registry-url": "^6.0.0",
|
||||
"spdx-license-list": "^6.4.0",
|
||||
"util": "^0.12.4",
|
||||
"yarn-programmatic": "^0.1.2"
|
||||
},
|
||||
"files": [
|
||||
"bin/",
|
||||
"src/",
|
||||
"templates/"
|
||||
]
|
||||
}
|
||||
55
platform/cli/src/commands/addExtension.js
Normal file
55
platform/cli/src/commands/addExtension.js
Normal file
@ -0,0 +1,55 @@
|
||||
import Listr from 'listr';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import {
|
||||
installNPMPackage,
|
||||
getYarnInfo,
|
||||
validateExtension,
|
||||
getVersionedPackageName,
|
||||
addExtensionToConfig,
|
||||
} from './utils/index.js';
|
||||
|
||||
export default async function addExtension(packageName, version) {
|
||||
console.log(chalk.green.bold(`Adding ohif-extension ${packageName}...`));
|
||||
|
||||
const versionedPackageName = getVersionedPackageName(packageName, version);
|
||||
|
||||
const tasks = new Listr(
|
||||
[
|
||||
{
|
||||
title: `Searching for extension: ${versionedPackageName}`,
|
||||
task: async () => await validateExtension(packageName, version),
|
||||
},
|
||||
{
|
||||
title: `Installing npm package: ${versionedPackageName}`,
|
||||
task: async () => await installNPMPackage(packageName, version),
|
||||
},
|
||||
{
|
||||
title: 'Adding ohif-extension to the configuration file',
|
||||
task: async ctx => {
|
||||
const yarnInfo = await getYarnInfo(packageName);
|
||||
|
||||
addExtensionToConfig(packageName, yarnInfo);
|
||||
|
||||
ctx.yarnInfo = yarnInfo;
|
||||
},
|
||||
},
|
||||
],
|
||||
{
|
||||
exitOnError: true,
|
||||
}
|
||||
);
|
||||
|
||||
await tasks
|
||||
.run()
|
||||
.then(ctx => {
|
||||
console.log(
|
||||
`${chalk.green.bold(
|
||||
`Added ohif-extension ${packageName}@${ctx.yarnInfo.version}`
|
||||
)} `
|
||||
);
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error.message);
|
||||
});
|
||||
}
|
||||
38
platform/cli/src/commands/addExtensions.js
Normal file
38
platform/cli/src/commands/addExtensions.js
Normal file
@ -0,0 +1,38 @@
|
||||
import Listr from 'listr';
|
||||
import chalk from 'chalk';
|
||||
import addExtension from './addExtension.js';
|
||||
|
||||
export default async function addExtensions(ohifExtensions) {
|
||||
// Auto generate Listr tasks...
|
||||
const taskEntries = [];
|
||||
|
||||
ohifExtensions.forEach(({ packageName, version }) => {
|
||||
const title = `Adding ohif-extension ${packageName}`;
|
||||
|
||||
taskEntries.push({
|
||||
title,
|
||||
task: async () => await addExtension(packageName, version),
|
||||
});
|
||||
});
|
||||
|
||||
const tasks = new Listr(taskEntries, {
|
||||
exitOnError: true,
|
||||
});
|
||||
|
||||
await tasks
|
||||
.run()
|
||||
.then(() => {
|
||||
let extensonsString = '';
|
||||
|
||||
ohifExtensions.forEach(({ packageName, version }) => {
|
||||
extensonsString += ` ${packageName}@${version}`;
|
||||
});
|
||||
|
||||
console.log(
|
||||
`${chalk.green.bold(`Extensions added:${extensonsString}`)} `
|
||||
);
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error.message);
|
||||
});
|
||||
}
|
||||
72
platform/cli/src/commands/addMode.js
Normal file
72
platform/cli/src/commands/addMode.js
Normal file
@ -0,0 +1,72 @@
|
||||
import Listr from 'listr';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import {
|
||||
installNPMPackage,
|
||||
getYarnInfo,
|
||||
getVersionedPackageName,
|
||||
validateMode,
|
||||
addModeToConfig,
|
||||
findRequiredOhifExtensionsForMode,
|
||||
} from './utils/index.js';
|
||||
import addExtensions from './addExtensions.js';
|
||||
|
||||
export default async function addMode(packageName, version) {
|
||||
console.log(chalk.green.bold(`Adding ohif-mode ${packageName}...`));
|
||||
|
||||
const versionedPackageName = getVersionedPackageName(packageName, version);
|
||||
|
||||
const tasks = new Listr(
|
||||
[
|
||||
{
|
||||
title: `Searching for mode: ${versionedPackageName}`,
|
||||
task: async () => await validateMode(packageName, version),
|
||||
},
|
||||
{
|
||||
title: `Installing npm package: ${versionedPackageName}`,
|
||||
task: async () => await installNPMPackage(packageName, version),
|
||||
},
|
||||
{
|
||||
title: 'Adding ohif-mode to the configuration file',
|
||||
task: async ctx => {
|
||||
const yarnInfo = await getYarnInfo(packageName);
|
||||
|
||||
addModeToConfig(packageName, yarnInfo);
|
||||
|
||||
ctx.yarnInfo = yarnInfo;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Detecting required ohif-extensions...',
|
||||
task: async ctx => {
|
||||
ctx.ohifExtensions = await findRequiredOhifExtensionsForMode(
|
||||
ctx.yarnInfo
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
{
|
||||
exitOnError: true,
|
||||
}
|
||||
);
|
||||
|
||||
await tasks
|
||||
.run()
|
||||
.then(async ctx => {
|
||||
console.log(
|
||||
`${chalk.green.bold(
|
||||
`Added ohif-mode ${packageName}@${ctx.yarnInfo.version}`
|
||||
)} `
|
||||
);
|
||||
|
||||
const ohifExtensions = ctx.ohifExtensions;
|
||||
|
||||
if (ohifExtensions.length) {
|
||||
console.log(`${chalk.green.bold(`Installing dependent extensions`)} `);
|
||||
await addExtensions(ohifExtensions);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error.message);
|
||||
});
|
||||
}
|
||||
1
platform/cli/src/commands/constants/notFound.js
Normal file
1
platform/cli/src/commands/constants/notFound.js
Normal file
@ -0,0 +1 @@
|
||||
export default 'Not found';
|
||||
97
platform/cli/src/commands/createPackage.js
Normal file
97
platform/cli/src/commands/createPackage.js
Normal file
@ -0,0 +1,97 @@
|
||||
import Listr from 'listr';
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs';
|
||||
|
||||
import {
|
||||
createDirectoryContents,
|
||||
editPackageJson,
|
||||
createLicense,
|
||||
createReadme,
|
||||
initGit,
|
||||
} from './utils/index.js';
|
||||
|
||||
const createPackage = async (options) => {
|
||||
const { packageType } = options; // extension or mode
|
||||
|
||||
if (fs.existsSync(options.targetDir)) {
|
||||
console.error(
|
||||
`%s ${packageType} with the same name already exists in this directory, either delete it or choose a different name`,
|
||||
chalk.red.bold('ERROR')
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
fs.mkdirSync(options.targetDir);
|
||||
|
||||
const tasks = new Listr(
|
||||
[
|
||||
{
|
||||
title: 'Copying template files',
|
||||
task: () =>
|
||||
createDirectoryContents(
|
||||
options.templateDir,
|
||||
options.targetDir,
|
||||
options.prettier
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Editing Package.json with provided information',
|
||||
task: () => editPackageJson(options),
|
||||
},
|
||||
{
|
||||
title: 'Creating a License file',
|
||||
task: () => createLicense(options),
|
||||
},
|
||||
{
|
||||
title: 'Creating a Readme file',
|
||||
task: () => createReadme(options),
|
||||
},
|
||||
{
|
||||
title: 'Initializing a Git Repository',
|
||||
enabled: () => options.gitRepository,
|
||||
task: () => initGit(options),
|
||||
},
|
||||
],
|
||||
{
|
||||
exitOnError: true,
|
||||
}
|
||||
);
|
||||
|
||||
await tasks.run();
|
||||
console.log();
|
||||
console.log(
|
||||
chalk.green(`Done: ${packageType} is ready at`, options.targetDir)
|
||||
);
|
||||
console.log();
|
||||
|
||||
console.log(
|
||||
chalk.green(`NOTE: In order to use this ${packageType} for development,`)
|
||||
);
|
||||
console.log(
|
||||
chalk.green(
|
||||
`run the following command inside the root of the OHIF monorepo`
|
||||
)
|
||||
);
|
||||
|
||||
console.log();
|
||||
console.log(
|
||||
chalk.green.bold(
|
||||
` yarn run cli link-${packageType} ${options.targetDir}`
|
||||
)
|
||||
);
|
||||
console.log();
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
"and when you don't need it anymore, run the following command to unlink it"
|
||||
)
|
||||
);
|
||||
console.log();
|
||||
console.log(
|
||||
chalk.yellow(` yarn run cli unlink-${packageType} ${options.name}`)
|
||||
);
|
||||
console.log();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
export default createPackage;
|
||||
8
platform/cli/src/commands/enums/colors.js
Normal file
8
platform/cli/src/commands/enums/colors.js
Normal file
@ -0,0 +1,8 @@
|
||||
const colors = {
|
||||
LIGHT: '#5acce6',
|
||||
MAIN: '#0944b3',
|
||||
DARK: '#090c29',
|
||||
ACTIVE: '#348cfd',
|
||||
};
|
||||
|
||||
export default colors;
|
||||
5
platform/cli/src/commands/enums/endPoints.js
Normal file
5
platform/cli/src/commands/enums/endPoints.js
Normal file
@ -0,0 +1,5 @@
|
||||
const endPoints = {
|
||||
NPM_KEYWORD: 'https://registry.npmjs.com/-/v1/search?text=keywords:',
|
||||
};
|
||||
|
||||
export default endPoints;
|
||||
5
platform/cli/src/commands/enums/index.js
Normal file
5
platform/cli/src/commands/enums/index.js
Normal file
@ -0,0 +1,5 @@
|
||||
import keywords from './keywords.js';
|
||||
import colors from './colors.js';
|
||||
import endPoints from './endPoints.js';
|
||||
|
||||
export { keywords, colors, endPoints };
|
||||
6
platform/cli/src/commands/enums/keywords.js
Normal file
6
platform/cli/src/commands/enums/keywords.js
Normal file
@ -0,0 +1,6 @@
|
||||
const keywords = {
|
||||
MODE: 'ohif-mode',
|
||||
EXTENSION: 'ohif-extension',
|
||||
};
|
||||
|
||||
export default keywords;
|
||||
23
platform/cli/src/commands/index.js
Normal file
23
platform/cli/src/commands/index.js
Normal file
@ -0,0 +1,23 @@
|
||||
import createPackage from './createPackage.js';
|
||||
import addExtension from './addExtension.js';
|
||||
import removeExtension from './removeExtension.js';
|
||||
import addMode from './addMode.js';
|
||||
import removeMode from './removeMode.js';
|
||||
import listPlugins from './listPlugins.js';
|
||||
import searchPlugins from './searchPlugins.js';
|
||||
import { linkExtension, linkMode } from './linkPackage.js';
|
||||
import { unlinkExtension, unlinkMode } from './unlinkPackage.js';
|
||||
|
||||
export {
|
||||
createPackage,
|
||||
addExtension,
|
||||
removeExtension,
|
||||
addMode,
|
||||
removeMode,
|
||||
listPlugins,
|
||||
searchPlugins,
|
||||
linkExtension,
|
||||
linkMode,
|
||||
unlinkExtension,
|
||||
unlinkMode,
|
||||
};
|
||||
56
platform/cli/src/commands/linkPackage.js
Normal file
56
platform/cli/src/commands/linkPackage.js
Normal file
@ -0,0 +1,56 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { execa } from 'execa';
|
||||
import { keywords } from './enums/index.js';
|
||||
import {
|
||||
validateYarn,
|
||||
addExtensionToConfig,
|
||||
addModeToConfig,
|
||||
} from './utils/index.js';
|
||||
|
||||
async function linkPackage(packageDir, options, addToConfig, keyword) {
|
||||
const { viewerDirectory } = options;
|
||||
|
||||
// read package.json from packageDir
|
||||
const file = fs.readFileSync(path.join(packageDir, 'package.json'), 'utf8');
|
||||
|
||||
// name of the package
|
||||
const packageJSON = JSON.parse(file);
|
||||
const packageName = packageJSON.name;
|
||||
const packageKeywords = packageJSON.keywords;
|
||||
|
||||
// check if package is an extension or a mode
|
||||
if (!packageKeywords.includes(keyword)) {
|
||||
throw new Error(`${packageName} is not ${keyword}`);
|
||||
}
|
||||
|
||||
const version = packageJSON.version;
|
||||
|
||||
// make sure yarn is installed
|
||||
await validateYarn();
|
||||
|
||||
// change directory to packageDir and execute yarn link
|
||||
process.chdir(packageDir);
|
||||
|
||||
let results;
|
||||
results = await execa(`yarn`, ['link']);
|
||||
|
||||
// change directory to OHIF Platform root and execute yarn link
|
||||
process.chdir(viewerDirectory);
|
||||
|
||||
results = await execa(`yarn`, ['link', packageName]);
|
||||
console.log(results.stdout);
|
||||
addToConfig(packageName, { version });
|
||||
}
|
||||
|
||||
function linkExtension(packageDir, options) {
|
||||
const keyword = keywords.EXTENSION;
|
||||
linkPackage(packageDir, options, addExtensionToConfig, keyword);
|
||||
}
|
||||
|
||||
function linkMode(packageDir, options) {
|
||||
const keyword = keywords.MODE;
|
||||
linkPackage(packageDir, options, addModeToConfig, keyword);
|
||||
}
|
||||
|
||||
export { linkExtension, linkMode };
|
||||
23
platform/cli/src/commands/listPlugins.js
Normal file
23
platform/cli/src/commands/listPlugins.js
Normal file
@ -0,0 +1,23 @@
|
||||
import fs from 'fs';
|
||||
import { prettyPrint } from './utils/index.js';
|
||||
import { colors } from './enums/index.js';
|
||||
|
||||
const listPlugins = async configPath => {
|
||||
const pluginConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||
|
||||
const { extensions, modes } = pluginConfig;
|
||||
|
||||
const titleOptions = { color: colors.LIGHT, bold: true };
|
||||
const itemsOptions = { color: colors.ACTIVE, bold: true };
|
||||
|
||||
const extensionsItems = extensions.map(
|
||||
extension => `${extension.packageName} @ ${extension.version}`
|
||||
);
|
||||
|
||||
const modesItems = modes.map(mode => `${mode.packageName} @ ${mode.version}`);
|
||||
|
||||
prettyPrint('Extensions', titleOptions, extensionsItems, itemsOptions);
|
||||
prettyPrint('Modes', titleOptions, modesItems, itemsOptions);
|
||||
};
|
||||
|
||||
export default listPlugins;
|
||||
49
platform/cli/src/commands/removeExtension.js
Normal file
49
platform/cli/src/commands/removeExtension.js
Normal file
@ -0,0 +1,49 @@
|
||||
import chalk from 'chalk';
|
||||
import Listr from 'listr';
|
||||
|
||||
import {
|
||||
uninstallNPMPackage,
|
||||
throwIfExtensionUsedByInstalledMode,
|
||||
removeExtensionFromConfig,
|
||||
validateExtensionYarnInfo,
|
||||
} from './utils/index.js';
|
||||
|
||||
export default async function removeExtension(packageName) {
|
||||
console.log(chalk.green.bold(`Removing ohif-extension ${packageName}...`));
|
||||
|
||||
const tasks = new Listr(
|
||||
[
|
||||
{
|
||||
title: `Searching for installed extension: ${packageName}`,
|
||||
task: async () => await validateExtensionYarnInfo(packageName),
|
||||
},
|
||||
{
|
||||
title: `Checking if ${packageName} is in use by an installed mode`,
|
||||
task: async () =>
|
||||
await throwIfExtensionUsedByInstalledMode(packageName),
|
||||
},
|
||||
{
|
||||
title: `Uninstalling npm package: ${packageName}`,
|
||||
task: async () => await uninstallNPMPackage(packageName),
|
||||
},
|
||||
{
|
||||
title: 'Removing ohif-extension from the configuration file',
|
||||
task: async () => removeExtensionFromConfig(packageName),
|
||||
},
|
||||
],
|
||||
{
|
||||
exitOnError: true,
|
||||
}
|
||||
);
|
||||
|
||||
await tasks
|
||||
.run()
|
||||
.then(() => {
|
||||
console.log(
|
||||
`${chalk.green.bold(`Removed ohif-extension ${packageName}`)} `
|
||||
);
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error.message);
|
||||
});
|
||||
}
|
||||
38
platform/cli/src/commands/removeExtensions.js
Normal file
38
platform/cli/src/commands/removeExtensions.js
Normal file
@ -0,0 +1,38 @@
|
||||
import Listr from 'listr';
|
||||
import chalk from 'chalk';
|
||||
import removeExtension from './removeExtension.js';
|
||||
|
||||
export default async function removeExtensions(ohifExtensionsToRemove) {
|
||||
// Auto generate Listr tasks...
|
||||
const taskEntries = [];
|
||||
|
||||
ohifExtensionsToRemove.forEach(packageName => {
|
||||
const title = `Removing ohif-extension ${packageName}`;
|
||||
|
||||
taskEntries.push({
|
||||
title,
|
||||
task: async () => await removeExtension(packageName),
|
||||
});
|
||||
});
|
||||
|
||||
const tasks = new Listr(taskEntries, {
|
||||
exitOnError: true,
|
||||
});
|
||||
|
||||
await tasks
|
||||
.run()
|
||||
.then(() => {
|
||||
let extensonsString = '';
|
||||
|
||||
ohifExtensionsToRemove.forEach(packageName => {
|
||||
extensonsString += ` ${packageName}`;
|
||||
});
|
||||
|
||||
console.log(
|
||||
`${chalk.green.bold(`Extensions removed:${extensonsString}`)} `
|
||||
);
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error.message);
|
||||
});
|
||||
}
|
||||
68
platform/cli/src/commands/removeMode.js
Normal file
68
platform/cli/src/commands/removeMode.js
Normal file
@ -0,0 +1,68 @@
|
||||
import Listr from 'listr';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import {
|
||||
uninstallNPMPackage,
|
||||
findOhifExtensionsToRemoveAfterRemovingMode,
|
||||
removeModeFromConfig,
|
||||
validateModeYarnInfo,
|
||||
getYarnInfo,
|
||||
} from './utils/index.js';
|
||||
import removeExtensions from './removeExtensions.js';
|
||||
|
||||
export default async function removeMode(packageName) {
|
||||
console.log(chalk.green.bold(`Removing ohif-mode ${packageName}...`));
|
||||
|
||||
const tasks = new Listr(
|
||||
[
|
||||
{
|
||||
title: `Searching for installed mode: ${packageName}`,
|
||||
task: async ctx => {
|
||||
ctx.yarnInfo = await getYarnInfo(packageName);
|
||||
await validateModeYarnInfo(packageName);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: `Uninstalling npm package: ${packageName}`,
|
||||
task: async () => await uninstallNPMPackage(packageName),
|
||||
},
|
||||
{
|
||||
title: 'Removing ohif-mode from the configuration file',
|
||||
task: async () => await removeModeFromConfig(packageName),
|
||||
},
|
||||
{
|
||||
title: 'Detecting extensions that can be removed...',
|
||||
task: async ctx => {
|
||||
ctx.ohifExtensionsToRemove = await findOhifExtensionsToRemoveAfterRemovingMode(
|
||||
ctx.yarnInfo
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
{
|
||||
exitOnError: true,
|
||||
}
|
||||
);
|
||||
|
||||
await tasks
|
||||
.run()
|
||||
.then(async ctx => {
|
||||
// Remove extensions if they aren't used by any other mode.
|
||||
console.log(`${chalk.green.bold(`Removed ohif-mode ${packageName}`)} `);
|
||||
|
||||
const ohifExtensionsToRemove = ctx.ohifExtensionsToRemove;
|
||||
|
||||
if (ohifExtensionsToRemove.length) {
|
||||
console.log(
|
||||
`${chalk.green.bold(
|
||||
`Removing ${ohifExtensionsToRemove.length} extensions no longer used by any installed mode`
|
||||
)}`
|
||||
);
|
||||
|
||||
await removeExtensions(ohifExtensionsToRemove);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error.message);
|
||||
});
|
||||
}
|
||||
57
platform/cli/src/commands/searchPlugins.js
Normal file
57
platform/cli/src/commands/searchPlugins.js
Normal file
@ -0,0 +1,57 @@
|
||||
import axios from 'axios';
|
||||
|
||||
import { prettyPrint } from './utils/index.js';
|
||||
import { keywords, colors, endPoints } from './enums/index.js';
|
||||
|
||||
async function searchRegistry(keyword) {
|
||||
const url = `${endPoints.NPM_KEYWORD}${keyword}`;
|
||||
|
||||
try {
|
||||
const response = await axios.get(url);
|
||||
const { objects } = response.data;
|
||||
return objects;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function searchPlugins(options) {
|
||||
const { verbose } = options;
|
||||
|
||||
const extensions = await searchRegistry(keywords.EXTENSION);
|
||||
const modes = await searchRegistry(keywords.MODE);
|
||||
|
||||
const titleOptions = { color: colors.LIGHT, bold: true };
|
||||
const itemsOptions = {};
|
||||
|
||||
const extensionsItems = extensions.map(extension => {
|
||||
const item = [
|
||||
`${extension.package.name} @ ${extension.package.version}`,
|
||||
[`Description: ${extension.package.description}`],
|
||||
];
|
||||
|
||||
if (verbose) {
|
||||
item[1].push(`Repository: ${extension.package.links.repository}`);
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
|
||||
const modesItems = modes.map(mode => {
|
||||
const item = [
|
||||
`${mode.package.name} @ ${mode.package.version}`,
|
||||
[`Description: ${mode.package.description}`],
|
||||
];
|
||||
|
||||
if (verbose) {
|
||||
item[1].push(`Repository: ${mode.package.links.repository}`);
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
|
||||
prettyPrint('Extensions', titleOptions, extensionsItems, itemsOptions);
|
||||
prettyPrint('Modes', titleOptions, modesItems, itemsOptions);
|
||||
}
|
||||
|
||||
export default searchPlugins;
|
||||
32
platform/cli/src/commands/unlinkPackage.js
Normal file
32
platform/cli/src/commands/unlinkPackage.js
Normal file
@ -0,0 +1,32 @@
|
||||
import { execa } from 'execa';
|
||||
import {
|
||||
validateYarn,
|
||||
removeExtensionFromConfig,
|
||||
removeModeFromConfig,
|
||||
} from './utils/index.js';
|
||||
|
||||
const linkPackage = async (packageName, options, removeFromConfig) => {
|
||||
const { viewerDirectory } = options;
|
||||
|
||||
// make sure yarn is installed
|
||||
await validateYarn();
|
||||
|
||||
// change directory to OHIF Platform root and execute yarn link
|
||||
process.chdir(viewerDirectory);
|
||||
|
||||
const results = await execa(`yarn`, ['unlink', packageName]);
|
||||
console.log(results.stdout);
|
||||
|
||||
//update the plugin.json file
|
||||
removeFromConfig(packageName);
|
||||
};
|
||||
|
||||
function unlinkExtension(extensionName, options) {
|
||||
linkPackage(extensionName, options, removeExtensionFromConfig);
|
||||
}
|
||||
|
||||
function unlinkMode(modeName, options) {
|
||||
linkPackage(modeName, options, removeModeFromConfig);
|
||||
}
|
||||
|
||||
export { unlinkExtension, unlinkMode };
|
||||
34
platform/cli/src/commands/utils/addToConfig.js
Normal file
34
platform/cli/src/commands/utils/addToConfig.js
Normal file
@ -0,0 +1,34 @@
|
||||
import {
|
||||
addExtensionToConfigJson,
|
||||
addModeToConfigJson,
|
||||
readPluginConfigFile,
|
||||
writePluginConfigFile,
|
||||
} from './private/index.js';
|
||||
|
||||
function addToAndOverwriteConfig(packageName, options, augmentConfigFunction) {
|
||||
const installedVersion = options.version;
|
||||
let pluginConfig = readPluginConfigFile();
|
||||
|
||||
if (!pluginConfig) {
|
||||
pluginConfig = {
|
||||
extensions: [],
|
||||
modes: [],
|
||||
};
|
||||
}
|
||||
|
||||
augmentConfigFunction(pluginConfig, {
|
||||
packageName,
|
||||
version: installedVersion,
|
||||
});
|
||||
writePluginConfigFile(pluginConfig);
|
||||
}
|
||||
|
||||
function addExtensionToConfig(packageName, options) {
|
||||
addToAndOverwriteConfig(packageName, options, addExtensionToConfigJson);
|
||||
}
|
||||
|
||||
function addModeToConfig(packageName, options) {
|
||||
addToAndOverwriteConfig(packageName, options, addModeToConfigJson);
|
||||
}
|
||||
|
||||
export { addExtensionToConfig, addModeToConfig };
|
||||
42
platform/cli/src/commands/utils/createDirectoryContents.js
Normal file
42
platform/cli/src/commands/utils/createDirectoryContents.js
Normal file
@ -0,0 +1,42 @@
|
||||
import fs from 'fs';
|
||||
|
||||
// https://github.dev/leoroese/template-cli/blob/628dd24db7df399ebb520edd0bc301bc7b5e8b66/index.js#L19
|
||||
const createDirectoryContents = (
|
||||
templatePath,
|
||||
targetDirPath,
|
||||
copyPrettierRules
|
||||
) => {
|
||||
const filesToCreate = fs.readdirSync(templatePath);
|
||||
|
||||
filesToCreate.forEach(file => {
|
||||
if (!copyPrettierRules && file === '.prettierrc') {
|
||||
return;
|
||||
}
|
||||
|
||||
const origFilePath = `${templatePath}/${file}`;
|
||||
|
||||
// get stats about the current file
|
||||
const stats = fs.statSync(origFilePath);
|
||||
|
||||
if (stats.isFile()) {
|
||||
const contents = fs.readFileSync(origFilePath, 'utf8');
|
||||
|
||||
// Rename
|
||||
if (file === '.npmignore') file = '.gitignore';
|
||||
|
||||
const writePath = `${targetDirPath}/${file}`;
|
||||
fs.writeFileSync(writePath, contents, 'utf8');
|
||||
} else if (stats.isDirectory()) {
|
||||
fs.mkdirSync(`${targetDirPath}/${file}`);
|
||||
|
||||
// recursive call
|
||||
createDirectoryContents(
|
||||
`${templatePath}/${file}`,
|
||||
`${targetDirPath}/${file}`,
|
||||
copyPrettierRules
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export default createDirectoryContents;
|
||||
31
platform/cli/src/commands/utils/createLicense.js
Normal file
31
platform/cli/src/commands/utils/createLicense.js
Normal file
@ -0,0 +1,31 @@
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { promisify } from 'util';
|
||||
import spdxLicenseList from 'spdx-license-list/full.js';
|
||||
|
||||
const writeFile = promisify(fs.writeFile);
|
||||
|
||||
async function createLicense(options) {
|
||||
const { targetDir, name, email } = options;
|
||||
const targetPath = path.join(targetDir, 'LICENSE');
|
||||
|
||||
let license;
|
||||
try {
|
||||
license = spdxLicenseList[options.license];
|
||||
} catch (err) {
|
||||
console.error(
|
||||
'%s License %s not found in the list of licenses',
|
||||
chalk.red.bold('ERROR'),
|
||||
options.license
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const licenseContent = license.licenseText
|
||||
.replace('<year>', new Date().getFullYear())
|
||||
.replace('<copyright holders>', `${name} (${email})`);
|
||||
return writeFile(targetPath, licenseContent, 'utf8');
|
||||
}
|
||||
|
||||
export default createLicense;
|
||||
23
platform/cli/src/commands/utils/createReadme.js
Normal file
23
platform/cli/src/commands/utils/createReadme.js
Normal file
@ -0,0 +1,23 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { promisify } from 'util';
|
||||
import mustache from 'mustache';
|
||||
|
||||
const writeFile = promisify(fs.writeFile);
|
||||
|
||||
async function createReadme(options) {
|
||||
let template = `# {{name}} \n## Description \n{{description}} \n## Author \n{{author}} \n## License \n{{license}}`;
|
||||
const { name, description, author, license, targetDir } = options;
|
||||
const targetPath = path.join(targetDir, 'README.md');
|
||||
|
||||
const readmeContent = mustache.render(template, {
|
||||
name,
|
||||
description,
|
||||
author,
|
||||
license,
|
||||
});
|
||||
|
||||
return writeFile(targetPath, readmeContent, 'utf8');
|
||||
}
|
||||
|
||||
export default createReadme;
|
||||
33
platform/cli/src/commands/utils/editPackageJson.js
Normal file
33
platform/cli/src/commands/utils/editPackageJson.js
Normal file
@ -0,0 +1,33 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
async function editPackageJson(options) {
|
||||
const { name, version, description, author, license, targetDir } = options;
|
||||
|
||||
// read package.json from targetDir
|
||||
const dependenciesPath = path.join(targetDir, 'dependencies.json');
|
||||
const rawData = fs.readFileSync(dependenciesPath, 'utf8');
|
||||
const packageJson = JSON.parse(rawData);
|
||||
|
||||
// edit package.json
|
||||
const mergedObj = Object.assign(
|
||||
{
|
||||
name,
|
||||
version,
|
||||
description,
|
||||
author,
|
||||
license,
|
||||
files: ['dist', 'README.md'],
|
||||
},
|
||||
packageJson
|
||||
);
|
||||
|
||||
// write package.json back to targetDir
|
||||
const writePath = path.join(targetDir, 'package.json');
|
||||
fs.writeFileSync(writePath, JSON.stringify(mergedObj, null, 2));
|
||||
|
||||
// remove the dependencies.json file
|
||||
fs.unlinkSync(dependenciesPath);
|
||||
}
|
||||
|
||||
export default editPackageJson;
|
||||
@ -0,0 +1,67 @@
|
||||
import { readPluginConfigFile } from './private/index.js';
|
||||
import getYarnInfo from './getYarnInfo.js';
|
||||
|
||||
export default async function findOhifExtensionsToRemoveAfterRemovingMode(
|
||||
removedModeYarnInfo
|
||||
) {
|
||||
const pluginConfig = readPluginConfigFile();
|
||||
|
||||
if (!pluginConfig) {
|
||||
// No other modes or extensions, no action item.
|
||||
return [];
|
||||
}
|
||||
|
||||
const { modes, extensions } = pluginConfig;
|
||||
|
||||
const registeredExtensions = extensions.map(
|
||||
extension => extension.packageName
|
||||
);
|
||||
// TODO this is not a function
|
||||
const ohifExtensionsOfMode = Object.keys(
|
||||
removedModeYarnInfo.peerDependencies
|
||||
).filter(peerDependency => registeredExtensions.includes(peerDependency));
|
||||
|
||||
const ohifExtensionsUsedInOtherModes = ohifExtensionsOfMode.map(
|
||||
packageName => {
|
||||
return {
|
||||
packageName,
|
||||
used: false,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Check if other modes use each extension used by this mode
|
||||
const otherModes = modes.filter(
|
||||
mode => mode.packageName !== removedModeYarnInfo.name
|
||||
);
|
||||
|
||||
for (let i = 0; i < otherModes.length; i++) {
|
||||
const mode = otherModes[i];
|
||||
const yarnInfo = await getYarnInfo(mode.packageName);
|
||||
|
||||
const peerDependencies = yarnInfo.peerDependencies;
|
||||
|
||||
if (!peerDependencies) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let j = 0; j < ohifExtensionsUsedInOtherModes.length; j++) {
|
||||
const ohifExtension = ohifExtensionsUsedInOtherModes[j];
|
||||
if (ohifExtension.used) {
|
||||
// Already accounted that we can't delete this, so don't waste effort
|
||||
return;
|
||||
}
|
||||
|
||||
if (Object.keys(peerDependencies).includes(ohifExtension.packageName)) {
|
||||
ohifExtension.used = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return list of now unused extensions
|
||||
const ohifExtensionsToRemove = ohifExtensionsUsedInOtherModes
|
||||
.filter(ohifExtension => !ohifExtension.used)
|
||||
.map(ohifExtension => ohifExtension.packageName);
|
||||
|
||||
return ohifExtensionsToRemove;
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
import { validateExtension } from './validate.js';
|
||||
|
||||
export default async function findRequiredOhifExtensionsForMode(yarnInfo) {
|
||||
// Get yarn info file and get peer dependencies
|
||||
if (!yarnInfo.peerDependencies) {
|
||||
// No ohif-extension dependencies
|
||||
return;
|
||||
}
|
||||
|
||||
const peerDependencies = yarnInfo.peerDependencies;
|
||||
const dependencies = [];
|
||||
const ohifExtensions = [];
|
||||
|
||||
Object.keys(peerDependencies).forEach((packageName) => {
|
||||
dependencies.push({
|
||||
packageName,
|
||||
version: peerDependencies[packageName],
|
||||
});
|
||||
});
|
||||
|
||||
const promises = [];
|
||||
|
||||
// Fetch each npm json and check which are ohif extensions
|
||||
for (let i = 0; i < dependencies.length; i++) {
|
||||
const dependency = dependencies[i];
|
||||
const { packageName, version } = dependency;
|
||||
const promise = validateExtension(packageName, version)
|
||||
.then(() => {
|
||||
ohifExtensions.push({ packageName, version });
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
promises.push(promise);
|
||||
}
|
||||
|
||||
// Await all the extensions // TODO -> Improve so we async install each
|
||||
// extension and await all of those promises instead.
|
||||
await Promise.all(promises);
|
||||
|
||||
return ohifExtensions;
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
export default function getVersionedPackageName(packageName, version) {
|
||||
return version === undefined ? packageName : `${packageName}@${version}`;
|
||||
}
|
||||
5
platform/cli/src/commands/utils/getYarnInfo.js
Normal file
5
platform/cli/src/commands/utils/getYarnInfo.js
Normal file
@ -0,0 +1,5 @@
|
||||
import { info } from 'yarn-programmatic';
|
||||
|
||||
export default async function getYarnInfo(packageName) {
|
||||
return await info(packageName);
|
||||
}
|
||||
50
platform/cli/src/commands/utils/index.js
Normal file
50
platform/cli/src/commands/utils/index.js
Normal file
@ -0,0 +1,50 @@
|
||||
import getVersionedPackageName from './getVersionedPackageName.js';
|
||||
import installNPMPackage from './installNPMPackage.js';
|
||||
import uninstallNPMPackage from './uninstallNPMPackage.js';
|
||||
import {
|
||||
validateMode,
|
||||
validateExtension,
|
||||
validateModeYarnInfo,
|
||||
validateExtensionYarnInfo,
|
||||
} from './validate.js';
|
||||
import getYarnInfo from './getYarnInfo.js';
|
||||
import { addExtensionToConfig, addModeToConfig } from './addToConfig.js';
|
||||
import findRequiredOhifExtensionsForMode from './findRequiredOhifExtensionsForMode.js';
|
||||
import {
|
||||
removeExtensionFromConfig,
|
||||
removeModeFromConfig,
|
||||
} from './removeFromConfig.js';
|
||||
import throwIfExtensionUsedByInstalledMode from './throwIfExtensionUsedByInstalledMode.js';
|
||||
import findOhifExtensionsToRemoveAfterRemovingMode from './findOhifExtensionsToRemoveAfterRemovingMode.js';
|
||||
import initGit from './initGit.js';
|
||||
import createDirectoryContents from './createDirectoryContents.js';
|
||||
import editPackageJson from './editPackageJson.js';
|
||||
import createLicense from './createLicense.js';
|
||||
import createReadme from './createReadme.js';
|
||||
import prettyPrint from './prettyPrint.js';
|
||||
import validateYarn from './validateYarn.js';
|
||||
|
||||
export {
|
||||
getYarnInfo,
|
||||
getVersionedPackageName,
|
||||
installNPMPackage,
|
||||
uninstallNPMPackage,
|
||||
validateMode,
|
||||
validateExtension,
|
||||
validateModeYarnInfo,
|
||||
validateExtensionYarnInfo,
|
||||
addExtensionToConfig,
|
||||
addModeToConfig,
|
||||
findRequiredOhifExtensionsForMode,
|
||||
removeExtensionFromConfig,
|
||||
throwIfExtensionUsedByInstalledMode,
|
||||
removeModeFromConfig,
|
||||
findOhifExtensionsToRemoveAfterRemovingMode,
|
||||
initGit,
|
||||
createDirectoryContents,
|
||||
editPackageJson,
|
||||
createLicense,
|
||||
createReadme,
|
||||
prettyPrint,
|
||||
validateYarn,
|
||||
};
|
||||
35
platform/cli/src/commands/utils/initGit.js
Normal file
35
platform/cli/src/commands/utils/initGit.js
Normal file
@ -0,0 +1,35 @@
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { promisify } from 'util';
|
||||
import { execa } from 'execa';
|
||||
|
||||
const exists = promisify(fs.exists);
|
||||
|
||||
async function initGit(options) {
|
||||
const { targetDir } = options;
|
||||
const targetPath = path.join(targetDir, '.git');
|
||||
|
||||
// Check if git is installed
|
||||
try {
|
||||
await execa('git', ['--version']);
|
||||
} catch (err) {
|
||||
console.error(
|
||||
'%s Git is not installed. Please install git and try again.',
|
||||
chalk.red.bold('ERROR')
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!(await exists(targetPath))) {
|
||||
try {
|
||||
await execa('git', ['init'], { cwd: targetDir });
|
||||
} catch (err) {
|
||||
console.error('%s Failed to initialize git', chalk.red.bold('ERROR'));
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default initGit;
|
||||
14
platform/cli/src/commands/utils/installNPMPackage.js
Normal file
14
platform/cli/src/commands/utils/installNPMPackage.js
Normal file
@ -0,0 +1,14 @@
|
||||
import { install } from 'pkg-install';
|
||||
|
||||
const installNPMPackage = async (packageName, version) => {
|
||||
let installObject = {};
|
||||
|
||||
installObject[packageName] = version;
|
||||
|
||||
await install(installObject, {
|
||||
prefer: 'yarn',
|
||||
cwd: process.cwd(),
|
||||
});
|
||||
};
|
||||
|
||||
export default installNPMPackage;
|
||||
79
platform/cli/src/commands/utils/prettyPrint.js
Normal file
79
platform/cli/src/commands/utils/prettyPrint.js
Normal file
@ -0,0 +1,79 @@
|
||||
import chalk from 'chalk';
|
||||
import { colors } from '../enums/index.js';
|
||||
|
||||
function getStyle({ color, bold }) {
|
||||
return bold ? chalk.hex(color).bold : chalk.hex(color);
|
||||
}
|
||||
|
||||
function levelOnePrint(items) {
|
||||
let output = '';
|
||||
if (Array.isArray(items)) {
|
||||
items.forEach(item => {
|
||||
output += ` |- ${item}\n`;
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
return ` |- ${items}\n`;
|
||||
}
|
||||
|
||||
function levelTwoPrint(items) {
|
||||
let output = '';
|
||||
items.forEach(item => {
|
||||
output += ` | |- ${item}\n`;
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} title Title of the section
|
||||
* @param {object} titleOptions Options for the title includes color and bold
|
||||
* @param { [] | [][] } items Array of items to display, OR a list of lists
|
||||
* @param {object} itemOptions Options for the items includes color and bold
|
||||
*
|
||||
*
|
||||
* items= ['Mode-A', 'Mode-B', 'Mode-C']
|
||||
*
|
||||
* |- Mode-A
|
||||
* |- Mode-B
|
||||
* |- Mode-C
|
||||
*
|
||||
* items = [['Mode-A', ['Description-A', 'Authors-A', 'Repository-A]], ['Mode-B', ['Description-B', 'Authors-B', 'Repository-B]], ['Mode-C', ['Description-C', 'Authors-C', 'Repository-C]]]
|
||||
*
|
||||
* |- Mode-A
|
||||
* | |- Description-A
|
||||
* | |- Authors-A
|
||||
* | |- Repository-A
|
||||
* |
|
||||
* |- Mode-B
|
||||
* | |- Description-B
|
||||
* | |- Authors-B
|
||||
* | |- Repository-B
|
||||
*
|
||||
*
|
||||
*/
|
||||
function prettyPrint(
|
||||
title,
|
||||
titleOptions = { color: colors.MAIN, bold: true },
|
||||
itemsArray = [[]],
|
||||
itemOptions = {}
|
||||
) {
|
||||
console.log('');
|
||||
console.log(getStyle(titleOptions)(title));
|
||||
|
||||
let output = '';
|
||||
itemsArray.forEach(items => {
|
||||
if (!Array.isArray(items)) {
|
||||
output += levelOnePrint(items);
|
||||
} else {
|
||||
output += levelOnePrint(items[0]);
|
||||
output += levelTwoPrint(items[1]);
|
||||
}
|
||||
});
|
||||
|
||||
const itmeStyle = itemOptions.color ? getStyle(itemOptions)(output) : output;
|
||||
console.log(itmeStyle);
|
||||
}
|
||||
|
||||
export default prettyPrint;
|
||||
@ -0,0 +1,15 @@
|
||||
export default function getPackageNameAndScope(packageName) {
|
||||
let scope;
|
||||
let packageNameLessScope;
|
||||
|
||||
if (packageName.includes('@')) {
|
||||
[scope, packageNameLessScope] = packageName.split('/');
|
||||
} else {
|
||||
packageNameLessScope = packageName;
|
||||
}
|
||||
|
||||
return {
|
||||
scope,
|
||||
packageNameLessScope,
|
||||
};
|
||||
}
|
||||
19
platform/cli/src/commands/utils/private/index.js
Normal file
19
platform/cli/src/commands/utils/private/index.js
Normal file
@ -0,0 +1,19 @@
|
||||
import getPackageNameAndScope from './getPackageNameAndScope.js';
|
||||
import {
|
||||
addExtensionToConfigJson,
|
||||
removeExtensionFromConfigJson,
|
||||
addModeToConfigJson,
|
||||
removeModeFromConfigJson,
|
||||
} from './manipulatePluginConfigFile.js';
|
||||
import writePluginConfigFile from './writePluginConfigFile.js';
|
||||
import readPluginConfigFile from './readPluginConfigFile.js';
|
||||
|
||||
export {
|
||||
getPackageNameAndScope,
|
||||
addExtensionToConfigJson,
|
||||
removeExtensionFromConfigJson,
|
||||
addModeToConfigJson,
|
||||
removeModeFromConfigJson,
|
||||
readPluginConfigFile,
|
||||
writePluginConfigFile,
|
||||
};
|
||||
@ -0,0 +1,40 @@
|
||||
function addExtensionToConfigJson(pluginConfig, { packageName, version }) {
|
||||
addToList('extensions', pluginConfig, { packageName, version });
|
||||
}
|
||||
|
||||
function addModeToConfigJson(pluginConfig, { packageName, version }) {
|
||||
addToList('modes', pluginConfig, { packageName, version });
|
||||
}
|
||||
|
||||
function removeExtensionFromConfigJson(pluginConfig, { packageName }) {
|
||||
removeFromList('extensions', pluginConfig, { packageName });
|
||||
}
|
||||
|
||||
function removeModeFromConfigJson(pluginConfig, { packageName }) {
|
||||
removeFromList('modes', pluginConfig, { packageName });
|
||||
}
|
||||
|
||||
function removeFromList(listName, pluginConfig, { packageName }) {
|
||||
const list = pluginConfig[listName];
|
||||
|
||||
const indexOfExistingEntry = list.findIndex(
|
||||
entry => entry.packageName === packageName
|
||||
);
|
||||
|
||||
if (indexOfExistingEntry !== -1) {
|
||||
pluginConfig[listName].splice(indexOfExistingEntry, 1);
|
||||
}
|
||||
}
|
||||
|
||||
function addToList(listName, pluginConfig, { packageName, version }) {
|
||||
removeFromList(listName, pluginConfig, { packageName });
|
||||
|
||||
pluginConfig[listName].push({ packageName, version });
|
||||
}
|
||||
|
||||
export {
|
||||
addExtensionToConfigJson,
|
||||
addModeToConfigJson,
|
||||
removeExtensionFromConfigJson,
|
||||
removeModeFromConfigJson,
|
||||
};
|
||||
@ -0,0 +1,17 @@
|
||||
import fs from 'fs';
|
||||
|
||||
export default function readPluginConfigFile() {
|
||||
let fileContents;
|
||||
|
||||
try {
|
||||
fileContents = fs.readFileSync('./pluginConfig.json', { flag: 'r' });
|
||||
} catch (err) {
|
||||
return; // File doesn't exist yet.
|
||||
}
|
||||
|
||||
if (fileContents) {
|
||||
fileContents = JSON.parse(fileContents);
|
||||
}
|
||||
|
||||
return fileContents;
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
import fs from 'fs';
|
||||
|
||||
export default function writePluginConfigFile(pluginConfig) {
|
||||
// Note: Second 2 arguments are to pretty print the JSON so its human readable.
|
||||
const jsonStringOfFileContents = JSON.stringify(pluginConfig, null, 2);
|
||||
|
||||
fs.writeFileSync(
|
||||
`./pluginConfig.json`,
|
||||
jsonStringOfFileContents,
|
||||
{ flag: 'w+' },
|
||||
(err) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
26
platform/cli/src/commands/utils/removeFromConfig.js
Normal file
26
platform/cli/src/commands/utils/removeFromConfig.js
Normal file
@ -0,0 +1,26 @@
|
||||
import {
|
||||
removeExtensionFromConfigJson,
|
||||
removeModeFromConfigJson,
|
||||
writePluginConfigFile,
|
||||
readPluginConfigFile,
|
||||
} from './private/index.js';
|
||||
|
||||
function removeFromAndOverwriteConfig(packageName, augmentConfigFunction) {
|
||||
const pluginConfig = readPluginConfigFile();
|
||||
|
||||
// Note: if file is not found, nothing to remove.
|
||||
if (pluginConfig) {
|
||||
augmentConfigFunction(pluginConfig, { packageName });
|
||||
writePluginConfigFile(pluginConfig);
|
||||
}
|
||||
}
|
||||
|
||||
function removeExtensionFromConfig(packageName) {
|
||||
removeFromAndOverwriteConfig(packageName, removeExtensionFromConfigJson);
|
||||
}
|
||||
|
||||
function removeModeFromConfig(packageName) {
|
||||
removeFromAndOverwriteConfig(packageName, removeModeFromConfigJson);
|
||||
}
|
||||
|
||||
export { removeExtensionFromConfig, removeModeFromConfig };
|
||||
@ -0,0 +1,48 @@
|
||||
import { readPluginConfigFile } from './private/index.js';
|
||||
import getYarnInfo from './getYarnInfo.js';
|
||||
import chalk from 'chalk';
|
||||
|
||||
export default async function throwIfExtensionUsedByInstalledMode(packageName) {
|
||||
const pluginConfig = readPluginConfigFile();
|
||||
|
||||
if (!pluginConfig) {
|
||||
// No other modes, not in use
|
||||
return false;
|
||||
}
|
||||
|
||||
const { modes } = pluginConfig;
|
||||
|
||||
const modesUsingExtension = [];
|
||||
|
||||
for (let i = 0; i < modes.length; i++) {
|
||||
const mode = modes[i];
|
||||
const modePackageName = mode.packageName;
|
||||
const yarnInfo = await getYarnInfo(modePackageName);
|
||||
|
||||
const peerDependencies = yarnInfo.peerDependencies;
|
||||
|
||||
if (!peerDependencies) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Object.keys(peerDependencies).includes(packageName)) {
|
||||
modesUsingExtension.push(modePackageName);
|
||||
}
|
||||
}
|
||||
|
||||
if (modesUsingExtension.length > 0) {
|
||||
let modesString = '';
|
||||
|
||||
modesUsingExtension.forEach(packageName => {
|
||||
modesString += ` ${packageName}`;
|
||||
});
|
||||
|
||||
const error = new Error(
|
||||
`${chalk.yellow.red(
|
||||
'Error'
|
||||
)} ohif-extension ${packageName} used by installed modes:${modesString}`
|
||||
);
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
12
platform/cli/src/commands/utils/uninstallNPMPackage.js
Normal file
12
platform/cli/src/commands/utils/uninstallNPMPackage.js
Normal file
@ -0,0 +1,12 @@
|
||||
import { remove } from 'yarn-programmatic';
|
||||
|
||||
const uninstallNPMPackage = async packageName => {
|
||||
// TODO - Anoyingly pkg-install doesn't seem to have uninstall.
|
||||
// So since we are using yarn we will just use yarn here, but the tool
|
||||
// is certainly less generic. But its a super minor issue.
|
||||
await remove(packageName).catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
export default uninstallNPMPackage;
|
||||
160
platform/cli/src/commands/utils/validate.js
Normal file
160
platform/cli/src/commands/utils/validate.js
Normal file
@ -0,0 +1,160 @@
|
||||
import registryUrl from 'registry-url';
|
||||
import keywords from '../enums/keywords.js';
|
||||
import { getPackageNameAndScope } from './private/index.js';
|
||||
import chalk from 'chalk';
|
||||
import fetch from 'node-fetch';
|
||||
import getYarnInfo from './getYarnInfo.js';
|
||||
import NOT_FOUND from '../constants/notFound.js';
|
||||
|
||||
async function validateMode(packageName, version) {
|
||||
return validate(packageName, version, keywords.MODE);
|
||||
}
|
||||
|
||||
async function validateExtension(packageName, version) {
|
||||
return validate(packageName, version, keywords.EXTENSION);
|
||||
}
|
||||
|
||||
async function validateModeYarnInfo(packageName) {
|
||||
return validateYarnInfo(packageName, keywords.MODE);
|
||||
}
|
||||
|
||||
async function validateExtensionYarnInfo(packageName) {
|
||||
return validateYarnInfo(packageName, keywords.EXTENSION);
|
||||
}
|
||||
|
||||
function validateYarnInfo(packageName, keyword) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
function rejectIfNotFound() {
|
||||
const error = new Error(
|
||||
`${chalk.red.bold('Error')} extension ${packageName} not installed`
|
||||
);
|
||||
reject(error);
|
||||
}
|
||||
|
||||
const packageInfo = await getYarnInfo(packageName).catch(() => {
|
||||
rejectIfNotFound();
|
||||
});
|
||||
|
||||
if (!packageInfo) {
|
||||
rejectIfNotFound();
|
||||
return;
|
||||
}
|
||||
|
||||
const { keywords } = packageInfo;
|
||||
const isValid = keywords && keywords.includes(keyword);
|
||||
|
||||
if (isValid) {
|
||||
resolve(true);
|
||||
} else {
|
||||
const error = new Error(
|
||||
`${chalk.red.bold('Error')} package ${packageName} is not an ${keyword}`
|
||||
);
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getVersion(json, version) {
|
||||
const versions = Object.keys(json.versions);
|
||||
// if no version is defined get the latest
|
||||
if (version === undefined) {
|
||||
return json['dist-tags'].latest;
|
||||
}
|
||||
|
||||
// Get and validate version if it is explicitly defined
|
||||
const allowMinorVersionUpgrade = version.startsWith('^');
|
||||
if (!allowMinorVersionUpgrade) {
|
||||
const isValidVersion = versions.includes(version);
|
||||
|
||||
if (!isValidVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
// Choose version based on the newer minor/patch versions
|
||||
const [majorVersion] = version
|
||||
.split('^')[1]
|
||||
.split('.')
|
||||
.map((v) => parseInt(v));
|
||||
|
||||
// Find the version that matches the major version, but is the latest minor version
|
||||
versions
|
||||
.filter((version) => parseInt(version.split('.')[0]) === majorVersion)
|
||||
.sort((a, b) => {
|
||||
const [majorA, minorA, patchA] = a.split('.').map((v) => parseInt(v));
|
||||
const [majorB, minorB, patchB] = b.split('.').map((v) => parseInt(v));
|
||||
|
||||
if (majorA === majorB) {
|
||||
if (minorA === minorB) {
|
||||
return patchB - patchA;
|
||||
}
|
||||
|
||||
return minorB - minorA;
|
||||
}
|
||||
|
||||
return majorB - majorA;
|
||||
});
|
||||
|
||||
if (versions.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
return versions[0];
|
||||
}
|
||||
|
||||
function validate(packageName, version, keyword) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const { scope } = getPackageNameAndScope(packageName);
|
||||
|
||||
// Gets the registry of the package. Scoped packages may not be using the global default.
|
||||
const registryUrlOfPackage = registryUrl(scope);
|
||||
|
||||
const response = await fetch(`${registryUrlOfPackage}${packageName}`);
|
||||
const json = await response.json();
|
||||
|
||||
if (json.error && json.error === NOT_FOUND) {
|
||||
const error = new Error(
|
||||
`${chalk.red.bold('Error')} package ${packageName} not found`
|
||||
);
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
const packageVersion = getVersion(json, version);
|
||||
|
||||
if (packageVersion) {
|
||||
const versionedJson = json.versions[packageVersion];
|
||||
const keywords = versionedJson.keywords;
|
||||
|
||||
const isValid = keywords && keywords.includes(keyword);
|
||||
|
||||
if (isValid) {
|
||||
resolve(true);
|
||||
} else {
|
||||
const error = new Error(
|
||||
`${chalk.red.bold(
|
||||
'Error'
|
||||
)} package ${packageName} is not an ${keyword}`
|
||||
);
|
||||
reject(error);
|
||||
}
|
||||
} else {
|
||||
// Particular version undefined
|
||||
const error = new Error(
|
||||
`${chalk.red.bold(
|
||||
'Error'
|
||||
)} version ${packageVersion} of package ${packageName} not found`
|
||||
);
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
validateMode,
|
||||
validateExtension,
|
||||
validateModeYarnInfo,
|
||||
validateExtensionYarnInfo,
|
||||
};
|
||||
14
platform/cli/src/commands/utils/validateYarn.js
Normal file
14
platform/cli/src/commands/utils/validateYarn.js
Normal file
@ -0,0 +1,14 @@
|
||||
import chalk from 'chalk';
|
||||
import { execa } from 'execa';
|
||||
|
||||
export default async function validateYarn() {
|
||||
try {
|
||||
await execa('yarn', ['--version']);
|
||||
} catch (err) {
|
||||
console.log(
|
||||
'%s Yarn is not installed, please install it before linking your extension',
|
||||
chalk.red.bold('ERROR')
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
210
platform/cli/src/index.js
Executable file
210
platform/cli/src/index.js
Executable file
@ -0,0 +1,210 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import program from 'commander';
|
||||
import inquirer from 'inquirer';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
import { getPathQuestions, getRepoQuestions } from './questions.js';
|
||||
import {
|
||||
createPackage,
|
||||
addExtension,
|
||||
removeExtension,
|
||||
addMode,
|
||||
removeMode,
|
||||
listPlugins,
|
||||
searchPlugins,
|
||||
linkExtension,
|
||||
linkMode,
|
||||
unlinkExtension,
|
||||
unlinkMode,
|
||||
} from './commands/index.js';
|
||||
import chalk from 'chalk';
|
||||
|
||||
const runningDirectory = process.cwd();
|
||||
const viewerDirectory = path.resolve(runningDirectory, 'platform/viewer');
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const packageJsonPath = path.join(runningDirectory, 'package.json');
|
||||
|
||||
try {
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
if (packageJson.name !== 'ohif-monorepo-root') {
|
||||
console.log(packageJson);
|
||||
console.log(
|
||||
chalk.red('ohif-cli must run from the root of the OHIF platform')
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(
|
||||
chalk.red('ohif-cli must run from the root of the OHIF platform')
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function _createPackage(packageType) {
|
||||
const pathQuestions = getPathQuestions(packageType);
|
||||
const repoQuestions = getRepoQuestions(packageType);
|
||||
|
||||
let pathAnswers;
|
||||
|
||||
const askPathQuestions = () => {
|
||||
inquirer.prompt(pathQuestions).then((answers) => {
|
||||
pathAnswers = answers;
|
||||
if (pathAnswers.confirm) {
|
||||
askRepoQuestions(answers.baseDir, answers.name);
|
||||
} else {
|
||||
askPathQuestions();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const askRepoQuestions = () => {
|
||||
inquirer.prompt(repoQuestions).then((repoAnswers) => {
|
||||
const answers = {
|
||||
...pathAnswers,
|
||||
...repoAnswers,
|
||||
};
|
||||
|
||||
const templateDir = path.join(__dirname, `../templates/${packageType}`);
|
||||
answers.templateDir = templateDir;
|
||||
answers.targetDir = path.join(answers.baseDir);
|
||||
answers.packageType = packageType;
|
||||
|
||||
createPackage(answers);
|
||||
});
|
||||
};
|
||||
|
||||
askPathQuestions();
|
||||
}
|
||||
|
||||
// Todo: inject with webpack
|
||||
program.version('2.0.7').description('OHIF CLI');
|
||||
|
||||
program
|
||||
.command('create-extension')
|
||||
.description('Create a new template extension')
|
||||
.action(() => {
|
||||
_createPackage('extension');
|
||||
});
|
||||
|
||||
program
|
||||
.command('create-mode')
|
||||
.description('Create a new template Mode')
|
||||
.action(() => {
|
||||
_createPackage('mode');
|
||||
});
|
||||
|
||||
program
|
||||
.command('add-extension <packageName> [version]')
|
||||
.description('Adds an ohif extension')
|
||||
.action((packageName, version) => {
|
||||
// change directory to viewer
|
||||
process.chdir(viewerDirectory);
|
||||
addExtension(packageName, version);
|
||||
});
|
||||
|
||||
program
|
||||
.command('remove-extension <packageName>')
|
||||
.description('removes an ohif extension')
|
||||
.action((packageName) => {
|
||||
// change directory to viewer
|
||||
process.chdir(viewerDirectory);
|
||||
removeExtension(packageName);
|
||||
});
|
||||
|
||||
program
|
||||
.command('add-mode <packageName> [version]')
|
||||
.description('Removes an ohif mode')
|
||||
.action((packageName, version) => {
|
||||
// change directory to viewer
|
||||
process.chdir(viewerDirectory);
|
||||
addMode(packageName, version);
|
||||
});
|
||||
|
||||
program
|
||||
.command('remove-mode <packageName>')
|
||||
.description('Removes an ohif mode')
|
||||
.action((packageName) => {
|
||||
// change directory to viewer
|
||||
process.chdir(viewerDirectory);
|
||||
removeMode(packageName);
|
||||
});
|
||||
|
||||
program
|
||||
.command('link-extension <packageDir>')
|
||||
.description(
|
||||
'Links a local OHIF extension to the Viewer to be used for development'
|
||||
)
|
||||
.action((packageDir) => {
|
||||
if (!fs.existsSync(packageDir)) {
|
||||
console.log(
|
||||
chalk.red(
|
||||
'The extension directory does not exist, please provide a valid directory'
|
||||
)
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
linkExtension(packageDir, { viewerDirectory });
|
||||
});
|
||||
|
||||
program
|
||||
.command('unlink-extension <extensionName>')
|
||||
.description('Unlinks a local OHIF extension from the Viewer')
|
||||
.action((extensionName) => {
|
||||
unlinkExtension(extensionName, { viewerDirectory });
|
||||
console.log(
|
||||
chalk.green(
|
||||
`Successfully unlinked extension ${extensionName} from the Viewer, don't forget to run yarn install --force`
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
program
|
||||
.command('link-mode <packageDir>')
|
||||
.description(
|
||||
'Links a local OHIF mode to the Viewer to be used for development'
|
||||
)
|
||||
.action((packageDir) => {
|
||||
if (!fs.existsSync(packageDir)) {
|
||||
console.log(
|
||||
chalk.red(
|
||||
'The mode directory does not exist, please provide a valid directory'
|
||||
)
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
linkMode(packageDir, { viewerDirectory });
|
||||
});
|
||||
|
||||
program
|
||||
.command('unlink-mode <modeName>')
|
||||
.description('Unlinks a local OHIF mode from the Viewer')
|
||||
.action((modeName) => {
|
||||
unlinkMode(modeName, { viewerDirectory });
|
||||
console.log(
|
||||
chalk.green(
|
||||
`Successfully unlinked mode ${modeName} from the Viewer, don't forget to run yarn install --force`
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
program
|
||||
.command('list')
|
||||
.description('List Added Extensions and Modes')
|
||||
.action(() => {
|
||||
const configPath = path.resolve(viewerDirectory, './pluginConfig.json');
|
||||
listPlugins(configPath);
|
||||
});
|
||||
|
||||
program
|
||||
.command('search')
|
||||
.option('-v, --verbose', 'Verbose output')
|
||||
.description('Search NPM for the list of Modes and Extensions')
|
||||
.action((options) => {
|
||||
searchPlugins(options);
|
||||
});
|
||||
|
||||
program.parse(process.argv);
|
||||
85
platform/cli/src/questions.js
Normal file
85
platform/cli/src/questions.js
Normal file
@ -0,0 +1,85 @@
|
||||
import path from 'path';
|
||||
|
||||
function getPathQuestions(packageType) {
|
||||
return [
|
||||
{
|
||||
type: 'input',
|
||||
name: 'name',
|
||||
message: `What is the name of your ${packageType}?`,
|
||||
validate: (input) => {
|
||||
if (!input) {
|
||||
return 'Please enter a name';
|
||||
}
|
||||
return true;
|
||||
},
|
||||
default: `my-${packageType}`,
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'baseDir',
|
||||
message: `What is the target path to create your ${packageType} (we recommend you do not use the OHIF ${packageType} folder (./${packageType}s) unless you are developing a core ${packageType}):`,
|
||||
validate: (input) => {
|
||||
if (!input) {
|
||||
console.log('Please provide a valid target directory path');
|
||||
return;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
filter: (input, answers) => {
|
||||
return path.resolve(input, answers.name);
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: `Please confirm the above path for generating the ${packageType} folder:`,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function getRepoQuestions(packageType) {
|
||||
return [
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'gitRepository',
|
||||
message: 'Should it be a git repository?',
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'prettier',
|
||||
message: 'Should it follow same prettier rules as OHIF?',
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'version',
|
||||
message: `What is the version of your ${packageType}?`,
|
||||
default: '0.0.1',
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'description',
|
||||
message: `What is the description of your ${packageType}?`,
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'author',
|
||||
message: `Who is the author of your ${packageType}?`,
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'email',
|
||||
message: 'What is your email address?',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'license',
|
||||
message: `What is the license of your ${packageType}?`,
|
||||
default: 'MIT',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export { getPathQuestions, getRepoQuestions };
|
||||
104
platform/cli/templates/extension/.gitignore
vendored
Normal file
104
platform/cli/templates/extension/.gitignore
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and *not* Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
8
platform/cli/templates/extension/.prettierrc
Normal file
8
platform/cli/templates/extension/.prettierrc
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 80,
|
||||
"proseWrap": "always",
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"singleQuote": true
|
||||
}
|
||||
48
platform/cli/templates/extension/.webpack/webpack.prod.js
Normal file
48
platform/cli/templates/extension/.webpack/webpack.prod.js
Normal file
@ -0,0 +1,48 @@
|
||||
const path = require('path');
|
||||
const pkg = require('../package.json');
|
||||
|
||||
const outputFile = 'index.umd.js';
|
||||
const rootDir = path.resolve(__dirname, '../');
|
||||
const outputFolder = path.join(__dirname, '../dist');
|
||||
|
||||
const config = {
|
||||
mode: 'production',
|
||||
entry: rootDir + '/' + pkg.module,
|
||||
devtool: 'inline-source-map',
|
||||
output: {
|
||||
path: outputFolder,
|
||||
filename: outputFile,
|
||||
library: pkg.name,
|
||||
libraryTarget: 'umd',
|
||||
umdNamedDefine: true,
|
||||
globalObject: "typeof self !== 'undefined' ? self : this",
|
||||
},
|
||||
externals: [
|
||||
{
|
||||
react: {
|
||||
root: 'React',
|
||||
commonjs2: 'react',
|
||||
commonjs: 'react',
|
||||
amd: 'react',
|
||||
},
|
||||
},
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /(\.jsx|\.js)$/,
|
||||
loader: 'babel-loader',
|
||||
exclude: /(node_modules|bower_components)/,
|
||||
resolve: {
|
||||
extensions: ['.js', '.jsx'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
modules: [path.resolve('./node_modules'), path.resolve('./src')],
|
||||
extensions: ['.json', '.js', '.jsx'],
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
41
platform/cli/templates/extension/babel.config.js
Normal file
41
platform/cli/templates/extension/babel.config.js
Normal file
@ -0,0 +1,41 @@
|
||||
module.exports = {
|
||||
plugins: ['inline-react-svg', '@babel/plugin-proposal-class-properties'],
|
||||
env: {
|
||||
test: {
|
||||
presets: [
|
||||
[
|
||||
// TODO: https://babeljs.io/blog/2019/03/19/7.4.0#migration-from-core-js-2
|
||||
'@babel/preset-env',
|
||||
{
|
||||
modules: 'commonjs',
|
||||
debug: false,
|
||||
},
|
||||
],
|
||||
'@babel/preset-react',
|
||||
],
|
||||
plugins: [
|
||||
'@babel/plugin-proposal-object-rest-spread',
|
||||
'@babel/plugin-syntax-dynamic-import',
|
||||
'@babel/plugin-transform-regenerator',
|
||||
'@babel/plugin-transform-runtime',
|
||||
],
|
||||
},
|
||||
production: {
|
||||
presets: [
|
||||
// WebPack handles ES6 --> Target Syntax
|
||||
['@babel/preset-env', { modules: false }],
|
||||
'@babel/preset-react',
|
||||
],
|
||||
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
|
||||
},
|
||||
development: {
|
||||
presets: [
|
||||
// WebPack handles ES6 --> Target Syntax
|
||||
['@babel/preset-env', { modules: false }],
|
||||
'@babel/preset-react',
|
||||
],
|
||||
plugins: ['react-hot-loader/babel'],
|
||||
ignore: ['**/*.test.jsx', '**/*.test.js', '__snapshots__', '__tests__'],
|
||||
},
|
||||
},
|
||||
};
|
||||
58
platform/cli/templates/extension/dependencies.json
Normal file
58
platform/cli/templates/extension/dependencies.json
Normal file
@ -0,0 +1,58 @@
|
||||
{
|
||||
"repository": "OHIF/Viewers",
|
||||
"keywords": ["ohif-extension"],
|
||||
"main": "dist/index.umd.js",
|
||||
"module": "src/index.js",
|
||||
"engines": {
|
||||
"node": ">=14",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1.18.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --debug --output-pathinfo",
|
||||
"dev:dicom-pdf": "yarn run dev",
|
||||
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
|
||||
"build:package": "yarn run build",
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^3.0.0",
|
||||
"@ohif/extension-default": "^1.0.1",
|
||||
"@ohif/extension-cornerstone": "^3.0.0",
|
||||
"@ohif/i18n": "^1.0.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-i18next": "^10.11.0",
|
||||
"react-router": "^6.2.1",
|
||||
"react-router-dom": "^6.2.1",
|
||||
"webpack": "^5.50.0",
|
||||
"webpack-merge": "^5.7.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.7.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.5.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.5.0",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.5.5",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||
"@babel/plugin-transform-arrow-functions": "^7.2.0",
|
||||
"@babel/plugin-transform-regenerator": "^7.4.5",
|
||||
"@babel/plugin-transform-runtime": "^7.5.0",
|
||||
"babel-plugin-inline-react-svg": "^2.0.1",
|
||||
"@babel/preset-env": "^7.5.0",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"babel-eslint": "^8.0.3",
|
||||
"babel-loader": "^8.0.0-beta.4",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"copy-webpack-plugin": "^10.2.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"dotenv": "^14.1.0",
|
||||
"eslint": "^5.0.1",
|
||||
"eslint-loader": "^2.0.0",
|
||||
"uglifyjs-webpack-plugin": "^1.2.7",
|
||||
"webpack": "^4.12.2",
|
||||
"webpack-cli": "^3.0.8"
|
||||
}
|
||||
}
|
||||
5
platform/cli/templates/extension/src/id.js
Normal file
5
platform/cli/templates/extension/src/id.js
Normal file
@ -0,0 +1,5 @@
|
||||
import packageJson from '../package.json';
|
||||
|
||||
const id = packageJson.name;
|
||||
|
||||
export { id };
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user