Skip to content

Commit ef93996

Browse files
committed
feat(Example): switch example runner to Vite devserver
1 parent c7d56ee commit ef93996

8 files changed

Lines changed: 249 additions & 186 deletions

File tree

Documentation/docs/develop_example.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Which means the sub-directory of the __Examples__ directory correspond to a grou
4747
4848
## Running an example
4949
50-
To simplify the development and execution of examples we have a script that let you list all the examples and run a given one using the webpack dev server which will automatically rebuild the code while running when you are editing any file that get involved in it.
50+
To simplify the development and execution of examples we have a script that lets you list all the examples and run a given one using the Vite dev server, which automatically rebuilds while you edit relevant files.
5151
5252
To list the examples you can run the following command:
5353
@@ -64,5 +64,5 @@ $ npm run example -- ConeSource
6464
Then the example could be seen at the following URL where standard debug tools could be used within your browser:
6565

6666
```sh
67-
http://localhost:9999/
67+
http://localhost:3000/
6868
```

Documentation/docs/develop_webxr.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,19 +132,19 @@ VTK.js supports rendering to most devices that support the WebXR API. Rendering
132132

133133
### Building and Running Examples
134134

135-
Webpack can be used to run examples on local XR hardware. The following command will
135+
Vite can be used to run examples on local XR hardware. The following command will
136136
serve the "AR" example over a self-signed HTTPS connection:
137137

138138
```
139139
path/to/vtk-js> npm run example:https -- AR
140140
...
141-
<i> [webpack-dev-server] Project is running at:
142-
<i> [webpack-dev-server] Loopback: https://localhost:9999/
143-
<i> [webpack-dev-server] On Your Network (IPv4): https://xxx.xxx.xxx.xxx:9999/
141+
VITE vX.Y.Z ready in XXX ms
142+
➜ Local: https://localhost:3000/
143+
Network: https://xxx.xxx.xxx.xxx:3000/
144144
```
145145

146146
The example can then be launched on the WebXR device by navigating in a compatible browser to the
147-
address given at `https://xxx.xxx.xxx.xxx:9999` in the output.
147+
address given at `https://xxx.xxx.xxx.xxx:3000` in the output.
148148

149149
### Emulating XR Hardware
150150

Utilities/ExampleRunner/.gitignore

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 141 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,161 @@
11
#! /usr/bin/env node
22

3-
/* eslint-disable */
4-
var { program } = require('commander');
5-
var path = require('path');
6-
var shell = require('shelljs');
7-
var fs = require('fs');
8-
var examples = {};
9-
var basePath = path.resolve('./Documentation');
10-
var webpackConfigPath = path.join(__dirname, 'webpack.config.js');
11-
var distDir = path.join(__dirname, 'dist');
12-
var buildConfig = require('./template-config.js');
13-
const rootPath = path.resolve(path.join(__dirname, '../..'));
14-
var webpackSettings = require(path.join(rootPath, 'webpack.settings.js'));
3+
/* eslint-disable no-console */
4+
const { program } = require('commander');
5+
const path = require('path');
6+
const fs = require('fs');
7+
8+
const REPO_ROOT = path.resolve(__dirname, '../..');
9+
const SOURCES_ROOT = path.join(REPO_ROOT, 'Sources');
10+
const EXAMPLES_ROOT = path.join(REPO_ROOT, 'Examples');
11+
const VITE_CONFIG_PATH = path.join(
12+
REPO_ROOT,
13+
'Utilities',
14+
'ExampleRunner',
15+
'vite.example.config.mjs'
16+
);
17+
const DEFAULT_HOST = '0.0.0.0';
18+
const DEFAULT_PORT = '3000';
1519

1620
program
1721
.option('--no-browser', 'Do not open the browser')
18-
.option('--server-type <type>', 'Specify http (default) or self-signed https for serving examples', 'http')
22+
.option(
23+
'--server-type <type>',
24+
'Specify http (default) or self-signed https for serving examples',
25+
'http'
26+
)
1927
.parse(process.argv);
2028

2129
const options = program.opts();
2230

23-
function getSplitedPath(filePath) {
24-
var a = filePath.split('/');
25-
var b = filePath.split('\\');
26-
return a.length > b.length ? a : b;
31+
function normalizePath(filePath) {
32+
return filePath.replace(/\\/g, '/');
33+
}
34+
35+
function isClassExample(relPath) {
36+
return /\/example\/index\.js$/.test(relPath);
2737
}
2838

29-
function validPath(str) {
30-
return str.replace(/\//g, path.sep);
39+
function isStandaloneExample(relPath) {
40+
const parts = relPath.split('/');
41+
return parts.length === 3 && parts[2] === 'index.js';
3142
}
3243

33-
// ----------------------------------------------------------------------------
34-
// Find examples
35-
// ----------------------------------------------------------------------------
36-
37-
if (webpackSettings.examples) {
38-
var filterExamples = [].concat(program.args).filter(i => !!i);
39-
var buildExample = filterExamples.length === 1;
40-
var exampleCount = 0;
41-
42-
console.log('\n=> Extract examples\n');
43-
webpackSettings.examples.forEach(function (entry) {
44-
const regexp = entry.regexp ? new RegExp(entry.regexp) : /example\/index.js$/;
45-
var fullPath = path.join(basePath, entry.path ? entry.path : entry);
46-
47-
// Single example use case
48-
examples[fullPath] = {};
49-
var currentExamples = examples[fullPath];
50-
shell.cd(fullPath);
51-
shell.find('.')
52-
.filter( function(file) {
53-
return file.match(regexp);
54-
})
55-
.forEach( function(file) {
56-
var fullPath = getSplitedPath(file),
57-
exampleName = fullPath.pop();
58-
59-
while (['index.js', 'example'].indexOf(exampleName) !== -1) {
60-
exampleName = fullPath.pop();
61-
}
62-
63-
if (!buildExample || filterExamples.indexOf(exampleName) !== -1) {
64-
currentExamples[exampleName] = './' + file;
65-
console.log(' -', exampleName, ':', file);
66-
exampleCount++;
67-
} else {
68-
console.log(' -', exampleName, ': SKIPPED');
69-
}
70-
});
44+
function walkFiles(dirPath, onFile) {
45+
if (!fs.existsSync(dirPath)) {
46+
return;
47+
}
48+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
49+
entries.forEach((entry) => {
50+
const fullPath = path.join(dirPath, entry.name);
51+
if (entry.isDirectory()) {
52+
walkFiles(fullPath, onFile);
53+
return;
54+
}
55+
onFile(fullPath);
7156
});
72-
console.log();
57+
}
7358

74-
if (exampleCount === 0) {
75-
examples = null;
76-
if (buildExample) {
77-
console.error(`=> Error: Did not find any examples matching ${filterExamples[0]}`);
78-
process.exit(1);
59+
function collectExamples() {
60+
const examples = {};
61+
62+
walkFiles(SOURCES_ROOT, (fullPath) => {
63+
if (!fullPath.endsWith('index.js')) {
64+
return;
7965
}
80-
}
66+
const relPath = normalizePath(path.relative(SOURCES_ROOT, fullPath));
67+
if (
68+
relPath.startsWith('Testing/') ||
69+
relPath === 'Testing' ||
70+
!isClassExample(relPath)
71+
) {
72+
return;
73+
}
74+
const exampleName = path.basename(path.dirname(path.dirname(fullPath)));
75+
examples[exampleName] = fullPath;
76+
});
77+
78+
walkFiles(EXAMPLES_ROOT, (fullPath) => {
79+
if (!fullPath.endsWith('index.js')) {
80+
return;
81+
}
82+
const relPath = normalizePath(path.relative(EXAMPLES_ROOT, fullPath));
83+
if (!isStandaloneExample(relPath)) {
84+
return;
85+
}
86+
const exampleName = path.basename(path.dirname(fullPath));
87+
examples[exampleName] = fullPath;
88+
});
89+
90+
return examples;
91+
}
8192

82-
if (buildExample) {
83-
var exBasePath = null;
84-
const exampleName = filterExamples[0];
85-
Object.keys(examples).forEach((exampleBasePath) => {
86-
if (examples[exampleBasePath][exampleName]) {
87-
exBasePath = exampleBasePath;
88-
}
89-
});
90-
91-
// console.log(exampleName, ' => ', exBasePath, examples[exBasePath][exampleName]);
92-
const conf = buildConfig(exampleName, validPath(examples[exBasePath][exampleName]), distDir, validPath(rootPath), validPath(exBasePath));
93-
shell.ShellString(conf).to(webpackConfigPath);
94-
shell.cd(exBasePath);
95-
shell.exec(`webpack serve --server-type ${options.serverType} --progress --config ${webpackConfigPath}`)
96-
} else {
97-
console.log('=> To run an example:')
98-
console.log(' $ npm run example -- PUT_YOUR_EXAMPLE_NAME_HERE\n');
93+
function printExamples(examples) {
94+
const names = Object.keys(examples).sort((a, b) => a.localeCompare(b));
95+
if (!names.length) {
96+
console.log('=> No examples found');
97+
return;
9998
}
10099

100+
console.log('\n=> Available examples\n');
101+
names.forEach((name) => {
102+
const relPath = normalizePath(path.relative(REPO_ROOT, examples[name]));
103+
console.log(` - ${name}: ${relPath}`);
104+
});
105+
console.log('\n=> To run an example:');
106+
console.log(' $ npm run example -- PUT_YOUR_EXAMPLE_NAME_HERE\n');
101107
}
108+
109+
async function runExample(exampleName, entryPath) {
110+
const env = { ...process.env };
111+
env.EXAMPLE_ENTRY = entryPath;
112+
env.EXAMPLE_NAME = exampleName;
113+
env.EXAMPLE_REPO_ROOT = REPO_ROOT;
114+
env.EXAMPLE_HOST = env.EXAMPLE_HOST || DEFAULT_HOST;
115+
env.EXAMPLE_PORT = env.EXAMPLE_PORT || DEFAULT_PORT;
116+
env.EXAMPLE_OPEN = options.browser ? '1' : '0';
117+
env.EXAMPLE_HTTPS = options.serverType === 'https' ? '1' : '0';
118+
119+
console.log(`\n=> Running example "${exampleName}"`);
120+
console.log(
121+
`=> Entry: ${normalizePath(path.relative(REPO_ROOT, entryPath))}\n`
122+
);
123+
124+
Object.assign(process.env, env);
125+
126+
const { createServer } = await import('vite');
127+
const server = await createServer({
128+
configFile: VITE_CONFIG_PATH,
129+
});
130+
await server.listen();
131+
server.printUrls();
132+
133+
const closeServer = async () => {
134+
await server.close();
135+
process.exit(0);
136+
};
137+
138+
process.on('SIGINT', closeServer);
139+
process.on('SIGTERM', closeServer);
140+
}
141+
142+
const examples = collectExamples();
143+
const requestedExample = (program.args || []).find((arg) => !!arg);
144+
145+
if (!requestedExample) {
146+
printExamples(examples);
147+
process.exit(0);
148+
}
149+
150+
const entryPath = examples[requestedExample];
151+
if (!entryPath) {
152+
console.error(
153+
`=> Error: Did not find any examples matching ${requestedExample}`
154+
);
155+
process.exit(1);
156+
}
157+
158+
runExample(requestedExample, entryPath).catch((err) => {
159+
console.error(err);
160+
process.exit(1);
161+
});

Utilities/ExampleRunner/index.html

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta
6+
name="viewport"
7+
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
8+
/>
9+
<title>VTK.js Example | __EXAMPLE_TITLE__</title>
10+
</head>
11+
<body>
12+
<script>
13+
window.global = window.global || {};
14+
</script>
15+
<script type="module">
16+
import 'virtual:vtk-example-entry';
17+
</script>
18+
</body>
19+
</html>

Utilities/ExampleRunner/template-config.js

Lines changed: 0 additions & 87 deletions
This file was deleted.

0 commit comments

Comments
 (0)