Skip to content

Commit

Permalink
feat: improved example runner to handle casing and partial match (#347)
Browse files Browse the repository at this point in the history
* feat: better example runner to handle cassing and partial match

* fix

* fix docs
  • Loading branch information
sedghi authored Jan 6, 2023
1 parent a9b20f4 commit 9e8fa12
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 45 deletions.
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

0 comments on commit 9e8fa12

Please sign in to comment.