Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improved example runner to handle casing and partial match #347

Merged
merged 3 commits into from
Jan 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"api-check": "api-extractor --debug run",
"build:update-api": "yarn run build && api-extractor run --local",
"prepublishOnly": "yarn run build",
"example": "node ../../utils/ExampleRunner/example-runner-cli.js",
"webpack:watch": "webpack --mode development --progress --watch --config ./.webpack/webpack.dev.js"
},
"peerDependencies": {
Expand Down
33 changes: 10 additions & 23 deletions packages/docs/docs/tutorials/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,32 +38,19 @@ being called.
You can also run each example locally. It should be noted that `Cornerstone3D` is a
monorepo and contains three packages (`core`, `tools`, `streaming-image-volume`). Examples
for each of these packages are included in the `examples` directory inside each package.
To run the example you need to change directory to the package root and run `yarn run example ExampleName` (this is a limitation
and we will be working on a better solution to run examples from the root of the monorepo).
You can run each example by using its name as an argument to the `example` script. For instance,
It should be noted that the example name is not case sensitive, and even it can
suggest the name of the example you are looking for if you make a typo.

```bash

1. Clone the repository
2. `yarn install`
3. Run example
- For `core` examples:
```bash
cd packages/core
yarn run example ExampleName
# for instance:
# yarn run example volumeAPI
```
- For `tools` examples:
```bash
cd packages/tools
yarn run example ExampleName
# for instance:
# yarn run example petCt
```
- For `streaming-image-volume` examples:
```bash
cd packages/streaming-image-volume
yarn run example ExampleName
```
3. `yarn run example petct` // this should be run from the root of the repository

```

:::note Important
Example names are case sensitive, and they match their folder name
Use the root of the repository as the working directory when running the example.
Previously, you had to run the example in each package directory. This is no longer the case.
:::
1 change: 0 additions & 1 deletion packages/streaming-image-volume-loader/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
"api-check": "api-extractor --debug run",
"build:update-api": "yarn run build && api-extractor run --local",
"prepublishOnly": "yarn run build",
"example": "node ../../utils/ExampleRunner/example-runner-cli.js",
"webpack:watch": "webpack --mode development --progress --watch --config ./.webpack/webpack.dev.js"
},
"dependencies": {
Expand Down
1 change: 0 additions & 1 deletion packages/tools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
"api-check": "api-extractor --debug run",
"build:update-api": "yarn run build && api-extractor run --local",
"prepublishOnly": "yarn run build",
"example": "node ../../utils/ExampleRunner/example-runner-cli.js",
"webpack:watch": "webpack --mode development --progress --watch --config ./.webpack/webpack.dev.js"
},
"dependencies": {
Expand Down
97 changes: 81 additions & 16 deletions utils/ExampleRunner/example-runner-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
var { program } = require('commander');
var path = require('path');
var shell = require('shelljs');
var fs = require('fs');
var examples = {};
var basePath = path.resolve('./Documentation');
var webpackConfigPath = path.join(
__dirname,
'./webpack-AUTOGENERATED.config.js'
Expand All @@ -24,37 +22,77 @@ const options = program.opts();
//var configFilePath = path.join(process.cwd(), options.config.replace(/\//g, path.sep));
//var configuration = require(configFilePath);

function getSplitedPath(filePath) {
function getSplittedPath(filePath) {
var a = filePath.split('/');
var b = filePath.split('\\');
return a.length > b.length ? a : b;
}

function validPath(str) {
return str.replace(/\\\\/g, "/");
return str.replace(/\\\\/g, '/');
}

// from https://github.com/systemed/iD/blob/1e78ee5c87669aac407c69493f3f532c823346ef/js/id/util.js#L97-L115
function levenshteinDistance(a, b) {
if (a.length === 0) return b.length;
if (b.length === 0) return a.length;
const matrix = [];
for (let i = 0; i <= b.length; i++) {
matrix[i] = [i];
}
for (let j = 0; j <= a.length; j++) {
matrix[0][j] = j;
}
for (let i = 1; i <= b.length; i++) {
for (let j = 1; j <= a.length; j++) {
if (b.charAt(i - 1) === a.charAt(j - 1)) {
matrix[i][j] = matrix[i - 1][j - 1];
} else {
matrix[i][j] = Math.min(
matrix[i - 1][j - 1] + 1, // substitution
Math.min(
matrix[i][j - 1] + 1, // insertion
matrix[i - 1][j] + 1
)
); // deletion
}
}
}
return matrix[b.length][a.length];
}

// ----------------------------------------------------------------------------
// Find examples
// ----------------------------------------------------------------------------

const configuration = {
examples: [{ path: '../examples', regexp: 'index.ts' }],
examples: [
{ path: 'packages/core/examples', regexp: 'index.ts' },
{ path: 'packages/tools/examples', regexp: 'index.ts' },
{
path: 'packages/streaming-image-volume-loader/examples',
regexp: 'index.ts',
},
],
};

if (configuration.examples) {
var filterExamples = [].concat(program.args).filter((i) => !!i);

var buildExample = filterExamples.length === 1;
var exampleCount = 0;
var closestExampleName = null;
var closestSimilarity = 100;
var filteredExampleCorrectCase = null;

console.log('\n=> Extract examples\n');
configuration.examples.forEach(function (entry) {
const regexp = entry.regexp
? new RegExp(entry.regexp)
: /example\/index.ts$/;
let fullPath = path.join(basePath, entry.path ? entry.path : entry);
let fullPath = path.join(rootPath, entry.path ? entry.path : entry);

console.warn(fullPath);
console.warn('', fullPath);

// Single example use case
examples[fullPath] = {};
Expand All @@ -63,41 +101,67 @@ if (configuration.examples) {
shell
.find('.')
.filter(function (file) {
console.warn(file);
return file.match(regexp);
})
.forEach(function (file) {
var fullPath = getSplitedPath(file);
var fullPath = getSplittedPath(file);
var exampleName = fullPath.pop();

console.warn(exampleName);
while (['index.ts', 'example'].indexOf(exampleName) !== -1) {
// make sure the matching of the name is not case sensitive
exampleName = fullPath.pop();
}

if (!buildExample || filterExamples.indexOf(exampleName) !== -1) {
if (
!buildExample ||
filterExamples
.map((i) => i.toLowerCase())
.indexOf(exampleName.toLowerCase()) !== -1
) {
currentExamples[exampleName] = './' + file;
console.log(' -', exampleName, ':', file);
exampleCount++;
console.log(' - Found example: ' + exampleName);
filteredExampleCorrectCase = exampleName;
} else {
console.log(' -', exampleName, ': SKIPPED');
// store the similarity of the example name to the filter name
// so that we can suggest the user the correct name later
var similarity = Math.max(
0,
levenshteinDistance(exampleName, filterExamples[0])
);

if (similarity < closestSimilarity) {
closestExampleName = exampleName;
closestSimilarity = similarity;
}
}
});
});

if (exampleCount === 0 && closestExampleName) {
console.log(
`\n=> Error: Did not find any examples matching ${filterExamples[0]}; Did you mean \x1b[32m${closestExampleName}\x1b[0m?\n`
);

process.exit(1);
}

if (exampleCount === 0) {
examples = null;
if (buildExample) {
console.error(
`=> Error: Did not find any examples matching ${filterExamples[0]}`
`\n=> Error: Did not find any examples matching ${filterExamples[0]}`
);
process.exit(1);
}
}

// say name of running example
console.log(`\n=> Running examples ${filterExamples.join(', ')}\n`);

if (buildExample) {
var exBasePath = null;
const exampleName = filterExamples[0];
const exampleName = filteredExampleCorrectCase;
Object.keys(examples).forEach((exampleBasePath) => {
if (examples[exampleBasePath][exampleName]) {
exBasePath = exampleBasePath;
Expand All @@ -111,7 +175,8 @@ if (configuration.examples) {
validPath(rootPath),
validPath(exBasePath)
);
// console.log('buildConfig result', conf);

// console.log('conf', conf);
shell.ShellString(conf).to(webpackConfigPath);
shell.cd(exBasePath);
shell.exec(`webpack serve --progress --config ${webpackConfigPath}`);
Expand Down
6 changes: 3 additions & 3 deletions utils/ExampleRunner/template-config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
const path = require('path');

const csRenderBasePath = path.resolve('../core/src/index');
const csToolsBasePath = path.resolve('../tools/src/index');
const csRenderBasePath = path.resolve('packages/core/src/index');
const csToolsBasePath = path.resolve('packages/tools/src/index');
const csStreamingBasePath = path.resolve(
'../streaming-image-volume-loader/src/index'
'packages/streaming-image-volume-loader/src/index'
);

module.exports = function buildConfig(
Expand Down