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

Implement stdin input #3312

Merged
merged 9 commits into from Jan 4, 2020
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 24 additions & 25 deletions cli/run/index.ts
Expand Up @@ -3,14 +3,14 @@ import relative from 'require-relative';
import { WarningHandler } from '../../src/rollup/types';
import mergeOptions, { GenericConfigObject } from '../../src/utils/mergeOptions';
import { getAliasName } from '../../src/utils/relativeId';
import { stdinName } from '../../src/utils/stdin';
import { handleError } from '../logging';
import batchWarnings from './batchWarnings';
import build from './build';
import loadConfigFile from './loadConfigFile';
import { stdinName, stdinPlugin } from './stdin';
import watch from './watch';

export default function runRollup (command: any) {
export default function runRollup(command: any) {
let inputSource;
if (command._.length > 0) {
if (command.input) {
Expand Down Expand Up @@ -86,40 +86,39 @@ export default function runRollup (command: any) {

if (command.watch) process.env.ROLLUP_WATCH = 'true';

loadConfigFile(configFile, command)
return loadConfigFile(configFile, command)
.then(configs => execute(configFile, configs, command))
.catch(handleError);
} else {
return execute(configFile, [{ input: null }] as any, command);
if (!command.input && (command.stdin || !process.stdin.isTTY)) {
command.input = stdinName;
}
return execute(configFile, [{ input: [] }], command);
}
}

function execute (configFile: string, configs: GenericConfigObject[], command: any) {
async function execute(
configFile: string,
configs: GenericConfigObject[],
command: any
): Promise<void> {
if (command.watch) {
watch(configFile, configs, command, command.silent);
} else {
let promise = Promise.resolve();
for (const config of configs) {
promise = promise.then(() => {
const warnings = batchWarnings();
handleMissingInput(command, config);
const { inputOptions, outputOptions, optionError } = mergeOptions({
command,
config,
defaultOnWarnHandler: warnings.add
});

if (optionError)
(inputOptions.onwarn as WarningHandler)({ code: 'UNKNOWN_OPTION', message: optionError });
return build(inputOptions, outputOptions, warnings, command.silent);
const warnings = batchWarnings();
const { inputOptions, outputOptions, optionError } = mergeOptions({
command,
config,
defaultOnWarnHandler: warnings.add
});
if (optionError) {
(inputOptions.onwarn as WarningHandler)({ code: 'UNKNOWN_OPTION', message: optionError });
}
if (command.stdin !== false) {
inputOptions.plugins!.push(stdinPlugin());
}
await build(inputOptions, outputOptions, warnings, command.silent);
}
return promise;
}
}

function handleMissingInput(command: any, config: GenericConfigObject) {
if (!(command.input || config.input || config.input === '' || process.stdin.isTTY)) {
command.input = stdinName;
}
}
37 changes: 37 additions & 0 deletions cli/run/stdin.ts
@@ -0,0 +1,37 @@
import { Plugin } from '../../src/rollup/types';

export const stdinName = '-';

let stdinResult: Promise<string> | null = null;

export function stdinPlugin(): Plugin {
return {
name: 'stdin',
resolveId(id) {
if (id === stdinName) {
return id;
}
},
load(id) {
if (id === stdinName) {
return stdinResult || (stdinResult = readStdin());
}
}
};
}

function readStdin(): Promise<string> {
return new Promise((resolve, reject) => {
const chunks: Buffer[] = [];
process.stdin.setEncoding('utf8');
process.stdin
.on('data', chunk => chunks.push(chunk))
.on('end', () => {
const result = chunks.join('');
resolve(result);
})
.on('error', err => {
reject(err);
});
});
}
23 changes: 23 additions & 0 deletions docs/01-command-line-reference.md
Expand Up @@ -253,6 +253,7 @@ Many options have command line equivalents. In those cases, any arguments passed
--silent Don't print warnings
--sourcemapExcludeSources Do not include source code in source maps
--sourcemapFile <file> Specify bundle position for source maps
--no-stdin do not read "-" from stdin
--strictDeprecations Throw errors for deprecated features
--no-treeshake Disable tree-shaking optimisations
--no-treeshake.annotations Ignore pure call annotations
Expand Down Expand Up @@ -306,3 +307,25 @@ npm run build -- --environment BUILD:development
```

then the config file will receive `process.env.INCLUDE_DEPS === 'true'` and `process.env.BUILD === 'development'`.

#### `--no-stdin`

Do not read files from `stdin`. Setting this flag will prevent piping content to Rollup and make sure Rollup interprets `-` as a regular file name instead of interpreting this as the name of `stdin`. See also [Reading a file from stdin](guide/en/#reading-a-file-from-stdin).

### Reading a file from stdin

When using the command line interface, Rollup can also read content from stdin:

```
echo "export const foo = 42;" | rollup --format cjs --file out.js
```

When this file contains imports, Rollup will try to resolve them relative to the current working directory. When a config file is used, Rollup will only use `stdin` as an entry point if the file name of the entry point is `-`. To read a non-entry-point file from stdin, just call it `-`, which is the file name that is used internally to reference `stdin`. I.e.

```js
import foo from "-";
```

in any file will prompt Rollup to try to read the imported file from `stdin` and assign the default export to `foo`. You can pass the [`--no-stdin`](guide/en/#--no-stdin) CLI flag to Rollup to treat `-` as a regular file name instead.

The JavaScript API will always treat `-` as a regular file name.
16 changes: 3 additions & 13 deletions src/rollup/index.ts
Expand Up @@ -13,7 +13,7 @@ import {
} from '../utils/error';
import { writeFile } from '../utils/fs';
import getExportMode from '../utils/getExportMode';
import mergeOptions, { GenericConfigObject } from '../utils/mergeOptions';
import mergeOptions, { ensureArray, GenericConfigObject } from '../utils/mergeOptions';
import { basename, dirname, isAbsolute, resolve } from '../utils/path';
import { PluginDriver } from '../utils/PluginDriver';
import { ANONYMOUS_OUTPUT_PLUGIN_PREFIX, ANONYMOUS_PLUGIN_PREFIX } from '../utils/pluginUtils';
Expand Down Expand Up @@ -80,16 +80,6 @@ function applyOptionHook(inputOptions: InputOptions, plugin: Plugin) {
return inputOptions;
}

function ensureArray<T>(items: (T | null | undefined)[] | T | null | undefined): T[] {
if (Array.isArray(items)) {
return items.filter(Boolean) as T[];
}
if (items) {
return [items];
}
return [];
}

function normalizePlugins(rawPlugins: any, anonymousPrefix: string): Plugin[] {
const plugins = ensureArray(rawPlugins);
for (let pluginIndex = 0; pluginIndex < plugins.length; pluginIndex++) {
Expand All @@ -112,8 +102,8 @@ function getInputOptions(rawInputOptions: GenericConfigObject): InputOptions {
if (optionError)
(inputOptions.onwarn as WarningHandler)({ message: optionError, code: 'UNKNOWN_OPTION' });

inputOptions = ensureArray(inputOptions.plugins).reduce(applyOptionHook, inputOptions);
inputOptions.plugins = normalizePlugins(inputOptions.plugins, ANONYMOUS_PLUGIN_PREFIX);
inputOptions = inputOptions.plugins!.reduce(applyOptionHook, inputOptions);
inputOptions.plugins = normalizePlugins(inputOptions.plugins!, ANONYMOUS_PLUGIN_PREFIX);

if (inputOptions.inlineDynamicImports) {
if (inputOptions.preserveModules)
Expand Down
7 changes: 3 additions & 4 deletions src/rollup/types.d.ts
Expand Up @@ -233,10 +233,9 @@ export type IsPureModule = (id: string) => boolean | null | undefined;

export type HasModuleSideEffects = (id: string, external: boolean) => boolean;

export type LoadHook = (
this: PluginContext,
id: string
) => Promise<SourceDescription | string | null> | SourceDescription | string | null;
type LoadResult = SourceDescription | string | null | undefined;

export type LoadHook = (this: PluginContext, id: string) => Promise<LoadResult> | LoadResult;

export type TransformResult = string | null | undefined | TransformSourceDescription;

Expand Down
5 changes: 1 addition & 4 deletions src/utils/defaultPlugin.ts
Expand Up @@ -2,14 +2,13 @@ import { Plugin, ResolveIdHook } from '../rollup/types';
import { error } from './error';
import { lstatSync, readdirSync, readFile, realpathSync } from './fs';
import { basename, dirname, isAbsolute, resolve } from './path';
import { readStdin, stdinName } from './stdin';

export function getRollupDefaultPlugin(preserveSymlinks: boolean): Plugin {
return {
name: 'Rollup Core',
resolveId: createResolveId(preserveSymlinks) as ResolveIdHook,
load(id) {
return id === stdinName ? readStdin() : readFile(id);
return readFile(id);
},
resolveFileUrl({ relativePath, format }) {
return relativeUrlMechanisms[format](relativePath);
Expand Down Expand Up @@ -59,8 +58,6 @@ function createResolveId(preserveSymlinks: boolean) {
});
}

if (source === stdinName) return source;

// external modules (non-entry modules that start with neither '.' or '/')
// are skipped at this stage.
if (importer !== undefined && !isAbsolute(source) && source[0] !== '.') return null;
Expand Down
30 changes: 15 additions & 15 deletions src/utils/mergeOptions.ts
Expand Up @@ -5,8 +5,6 @@ import {
WarningHandlerWithDefault
} from '../rollup/types';

import { stdinName } from './stdin';

export interface GenericConfigObject {
[key: string]: unknown;
}
Expand Down Expand Up @@ -50,6 +48,16 @@ const getObjectOption = (
return configOption;
};

export function ensureArray<T>(items: (T | null | undefined)[] | T | null | undefined): T[] {
if (Array.isArray(items)) {
return items.filter(Boolean) as T[];
}
if (items) {
return [items];
}
return [];
}

const defaultOnWarn: WarningHandler = warning => {
if (typeof warning === 'string') {
console.warn(warning);
Expand Down Expand Up @@ -150,7 +158,8 @@ export default function mergeOptions({
Object.keys(commandAliases),
'config',
'environment',
'silent'
'silent',
'stdin'
),
'CLI flag',
/^_|output|(config.*)$/
Expand Down Expand Up @@ -210,8 +219,6 @@ function getInputOptions(
defaultOnWarnHandler: WarningHandler
): InputOptions {
const getOption = createGetOption(config, command);
const input = getOption('input', []);

const inputOptions: InputOptions = {
acorn: config.acorn,
acornInjectPlugins: config.acornInjectPlugins as any,
Expand All @@ -223,12 +230,12 @@ function getInputOptions(
experimentalTopLevelAwait: getOption('experimentalTopLevelAwait'),
external: getExternal(config, command) as any,
inlineDynamicImports: getOption('inlineDynamicImports', false),
input,
input: getOption('input', []),
manualChunks: getOption('manualChunks'),
moduleContext: config.moduleContext as any,
onwarn: getOnWarn(config, defaultOnWarnHandler),
perf: getOption('perf', false),
plugins: config.plugins as any,
plugins: ensureArray(config.plugins as any),
preserveModules: getOption('preserveModules'),
preserveSymlinks: getOption('preserveSymlinks'),
shimMissingExports: getOption('shimMissingExports'),
Expand All @@ -237,13 +244,6 @@ function getInputOptions(
watch: config.watch as any
};

if (
config.watch &&
(input === stdinName || (Array.isArray(input) && input.indexOf(stdinName) >= 0))
) {
throw new Error('watch mode is incompatible with stdin input');
}

// support rollup({ cache: prevBuildObject })
if (inputOptions.cache && (inputOptions.cache as any).cache)
inputOptions.cache = (inputOptions.cache as any).cache;
Expand Down Expand Up @@ -295,7 +295,7 @@ function getOutputOptions(
noConflict: getOption('noConflict'),
outro: getOption('outro'),
paths: getOption('paths'),
plugins: config.plugins as any,
plugins: ensureArray(config.plugins as any),
preferConst: getOption('preferConst'),
sourcemap: getOption('sourcemap'),
sourcemapExcludeSources: getOption('sourcemapExcludeSources'),
Expand Down
54 changes: 0 additions & 54 deletions src/utils/stdin.ts

This file was deleted.

Expand Up @@ -5,8 +5,7 @@ module.exports = {
plugins: [
{
transform(code, id) {
console.log(id);
if (id.endsWith('/dep1.js') || id.endsWith('/dep2.js')) {
if (id.endsWith('dep1.js') || id.endsWith('dep2.js')) {
return {
code,
syntheticNamedExports: true
Expand Down
Expand Up @@ -5,7 +5,7 @@ module.exports = {
plugins: [
{
resolveId(id) {
if (id === './dep1.js') {
if (id.endsWith('dep1.js')) {
return {
id,
syntheticNamedExports: true
Expand Down