Skip to content

Commit 192659d

Browse files
authored
feat(typeless-sample-bot): add output path flag, and another pass at module resolution fixing (#3498)
* feat: add outputpath flag to typeless sample bot * fix: another try at the Babel plugin loading logic * chore: remove unused import * chore: formatting fixes
1 parent c063aef commit 192659d

6 files changed

Lines changed: 107 additions & 29 deletions

File tree

packages/typeless-sample-bot/__snapshots__/index.js

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/typeless-sample-bot/src/app.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ async function processArgs(args: string[]) {
5959
describe: 'process the target(s) as directories, recursively',
6060
alias: ['r'],
6161
},
62+
outputpath: {
63+
demandOption: false,
64+
type: 'string',
65+
describe:
66+
'outputs default to being next to the original; if set, this option will specify the output for all files',
67+
alias: ['o'],
68+
},
6269
verbose: {
6370
demandOption: false,
6471
default: false,
@@ -155,7 +162,7 @@ export async function main(args: string[]): Promise<number> {
155162
const transformed = transformSamples(filtered);
156163

157164
// Write out all of the output samples.
158-
const written = writeSamples(transformed);
165+
const written = writeSamples(transformed, argv.outputpath);
159166

160167
try {
161168
// Wait for the pipeline to complete.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Presets are loaded by proxy here so that we can circumvent the Babel
16+
// path resolution process during load.
17+
18+
import psTypescript from '@babel/preset-typescript';
19+
20+
export const typescript = psTypescript;

packages/typeless-sample-bot/src/samples.ts

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ import loggers from './loggers.js';
1919
import {treeWalk} from './tree-walk.js';
2020
import {readFile, writeFile} from 'fs/promises';
2121
import babel from '@babel/core';
22-
import {fileURLToPath} from 'node:url';
2322
import path from 'node:path';
23+
import {typescript as presetTypescript} from './preset-loader.js';
24+
import importToRequire from './import-to-require.js';
2425

2526
// Converts an async iterable into an array of the same type.
2627
export async function toArray<T>(iterable: AsyncIterable<T>): Promise<T[]> {
@@ -80,25 +81,9 @@ export async function* filterByContents(
8081

8182
// Instead of a babelrc, this is used so that we can get more control over
8283
// the transform process.
83-
//
84-
// The heavy path manipulation is required here to work around a problem
85-
// with finding Babel plugins on the path when running the bot from
86-
// outside its own tree. Babel will attempt to resolve plugins to
87-
// the most proximate node_modules, which will be the package being
88-
// operated upon, not us.
89-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
90-
const ptPath = path.join(
91-
__dirname,
92-
'..',
93-
'..',
94-
'node_modules',
95-
'@babel',
96-
'preset-typescript'
97-
);
98-
const itrPath = path.join(__dirname, 'import-to-require');
9984
const babelConfig = {
100-
presets: [[ptPath, {}]],
101-
plugins: [[itrPath]],
85+
presets: [[presetTypescript, {}]],
86+
plugins: [[importToRequire]],
10287
parserOpts: {} as babel.ParserOptions,
10388
generatorOpts: {
10489
// Ensures that Babel keeps newlines so that comments end up
@@ -124,12 +109,22 @@ export async function* transformSamples(
124109

125110
// Write out all samples to the file system.
126111
export async function* writeSamples(
127-
samples: AsyncIterable<Sample>
112+
samples: AsyncIterable<Sample>,
113+
outputPath?: string
128114
): AsyncIterable<Sample> {
129115
for await (const s of samples) {
130-
loggers.verbose('writing new sample', s.filename);
131-
await writeFile(s.filename, s.contents);
132-
yield s;
116+
// If requested, rewrite the output path to be elsewhere.
117+
const newName = outputPath
118+
? path.join(outputPath, path.basename(s.filename))
119+
: s.filename;
120+
121+
loggers.verbose('writing new sample', newName);
122+
await writeFile(newName, s.contents);
123+
124+
yield {
125+
filename: newName,
126+
contents: s.contents,
127+
};
133128
}
134129
}
135130

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// This is needed to work around Babel's preset loading logic, since we
16+
// may need to run from a path that's not our node_modules.
17+
declare module '@babel/preset-typescript';

packages/typeless-sample-bot/test/index.ts

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {describe, it} from 'mocha';
1717
import snapshot from 'snap-shot-it';
1818
import * as samples from '../src/samples.js';
1919
import * as main from '../src/app.js';
20-
import {readFile, rm, stat} from 'node:fs/promises';
20+
import {readFile, rm, stat, mkdir} from 'node:fs/promises';
2121
import * as url from 'node:url';
2222
import * as path from 'node:path';
2323
import * as sinon from 'sinon';
@@ -29,7 +29,7 @@ const dirName = url.fileURLToPath(new URL('.', import.meta.url));
2929
const fixturePath = path.join(dirName, '..', '..', 'test', 'fixtures');
3030

3131
async function loadFixture(name: string): Promise<string> {
32-
const fn = path.join(fixturePath, name);
32+
const fn = name.includes(path.sep) ? name : path.join(fixturePath, name);
3333
return (await readFile(fn)).toString();
3434
}
3535

@@ -121,10 +121,41 @@ describe('command line option', () => {
121121
}
122122
});
123123

124+
it('"outputpath" causes the output to move', async () => {
125+
const testOutputPath = path.join(fixturePath, 'alternate');
126+
const cmdline = [
127+
'node',
128+
'index.ts',
129+
'--targets',
130+
path.join(fixturePath, 'deleteSchema.ts'),
131+
path.join(fixturePath, 'getSchema.ts'),
132+
'--outputpath',
133+
testOutputPath,
134+
];
135+
const outputs = [
136+
path.join(testOutputPath, 'deleteSchema.js'),
137+
path.join(testOutputPath, 'getSchema.js'),
138+
];
139+
try {
140+
await mkdir(testOutputPath);
141+
const retcode = await main.main(cmdline);
142+
assert.strictEqual(retcode, 0);
143+
const contents = [
144+
await loadFixture(outputs[0]),
145+
await loadFixture(outputs[1]),
146+
];
147+
snapshot(contents);
148+
} finally {
149+
rm(testOutputPath, {
150+
recursive: true,
151+
}).catch(() => {});
152+
}
153+
});
154+
124155
it('"recursive" causes recursion and only matches samples', async () => {
125156
const targets = [
126-
path.join('folder', 'listenForAvroRecords.js'),
127-
path.join('folder', 'publishAvroRecords.js'),
157+
path.join(fixturePath, 'folder', 'listenForAvroRecords.js'),
158+
path.join(fixturePath, 'folder', 'publishAvroRecords.js'),
128159
];
129160
const antiTarget = path.join(fixturePath, 'folder', 'validateSchema.js');
130161
try {
@@ -138,7 +169,10 @@ describe('command line option', () => {
138169
const retcode = await main.main(cmdline);
139170
assert.strictEqual(retcode, 0);
140171

141-
const contents = await Promise.all(targets.map(loadFixture));
172+
const contents = [
173+
await loadFixture(targets[0]),
174+
await loadFixture(targets[1]),
175+
];
142176
snapshot(contents);
143177

144178
await assert.rejects(stat(antiTarget));

0 commit comments

Comments
 (0)