Skip to content
Open
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
47 changes: 22 additions & 25 deletions src/helpers.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,11 @@
import path from 'path'
import fs from 'fs'

/**
* Formats an output file, which re-exports items from a list of paths.
* @param {*} config the rollup-plugin-flow-entry config object.
* @param {string} outDir the output directory.
* @param {string} fileName the output file name.
* @param {string[]} paths an array of absolute paths to export types from.
*/
export function buildEntry(config, outDir, fileName, paths) {
const { mode, types } = config

// Handle path overrides:
if (typeof types === 'string') {
paths = [types]
} else if (Array.isArray(types)) {
paths = types
} else if (typeof types === 'object' && types != null) {
const ourTypes = types[fileName]
if (typeof ourTypes === 'string') {
paths = [ourTypes]
} else if (Array.isArray(ourTypes)) {
paths = ourTypes
} else if (ourTypes === false) {
return
}
}
export function buildAsset(outDir, flowEntry, mode) {
const { fileName, input } = flowEntry

// Set up the path resolution logic:
const here = path.dirname(path.resolve(outDir, fileName))
Expand All @@ -38,11 +19,27 @@ export function buildEntry(config, outDir, fileName, paths) {

// Build the source code:
let source = mode != null ? `// @flow ${mode}\n\n` : '// @flow\n\n'
for (let i = 0; i < paths.length; ++i) {
source += `export * from '${escapePath(paths[i])}'\n`
if (typeof input === 'string') {
const sourceText = fs.readFileSync(input, 'utf8')
if (hasDefaultExport(sourceText)) {
source += `export { default } from '${escapePath(input)}'\n`
}
source += `export * from '${escapePath(input)}'\n`
} else {
for (let i = 0; i < input.length; ++i) {
source += `export * from '${escapePath(input[i])}'\n`
}
}

return { fileName: fileName + '.flow', isAsset: true, source }
return { fileName, isAsset: true, source }
}

/**
* Returns true if the source code contains "export default".
*/
function hasDefaultExport(sourceText) {
// TODO: Use a proper AST
return /export\s+default/.test(sourceText)
}

/**
Expand Down
50 changes: 36 additions & 14 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'path'

import { buildEntry, parseMultiEntry } from './helpers.js'
import { parseMultiEntry, buildAsset } from './helpers.js'

const multiEntryId = '\0rollup-plugin-multi-entry:entry-point'

Expand All @@ -18,35 +18,57 @@ export default function flowEntry(config = {}) {
generateBundle(opts, bundle) {
const outDir = opts.dir != null ? opts.dir : path.dirname(opts.file)

// A list of Flow entries we need to generate:
const entries = []
function pushFlowEntry(fileName, input) {
const { types } = config
if (typeof types === 'string') {
input = path.resolve(types)
} else if (Array.isArray(types)) {
input = types.map(p => path.resolve(p))
} else if (typeof types === 'object' && types != null) {
const ourTypes = types[fileName]
if (typeof ourTypes === 'string') {
input = path.resolve(ourTypes)
} else if (Array.isArray(ourTypes)) {
input = ourTypes.map(p => path.resolve(p))
} else if (ourTypes === false) {
return
}
}
entries.push({ fileName: fileName + '.flow', input })
}

// Find the bundle outputs that need flow entries:
for (const n in bundle) {
const file = bundle[n]
if (file.isAsset || !file.isEntry || file.facadeModuleId == null) {
continue
}

if (file.facadeModuleId !== multiEntryId) {
// Normal files:
const entry = buildEntry(config, outDir, file.fileName, [
file.facadeModuleId
])
if (entry != null) bundle[entry.fileName] = entry
} else {
// rollup-plugin-multi-entry:
// rollup-plugin-multi-entry:
if (file.facadeModuleId === multiEntryId) {
if (savedMultiEntry == null || opts.file == null) {
this.warn(
'Unable to create Flow entry: rollup-plugin-multi-entry not configured correctly'
)
continue
}

const entry = buildEntry(
config,
outDir,
pushFlowEntry(
path.basename(opts.file),
parseMultiEntry(outDir, savedMultiEntry)
)
if (entry != null) bundle[entry.fileName] = entry
continue
}

// Normal files:
pushFlowEntry(file.fileName, file.facadeModuleId)
}

// Generate the entries:
for (const entry of entries) {
const asset = buildAsset(outDir, entry, config.mode)
bundle[asset.fileName] = asset
}
}
}
Expand Down
1 change: 1 addition & 0 deletions test/demo/types/entry.js.flow
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
declare export default 'some string'
43 changes: 20 additions & 23 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { rollup } from 'rollup'
import babel from 'rollup-plugin-babel'
import multiEntry from 'rollup-plugin-multi-entry'

import { buildEntry } from '../src/helpers.js'
import { buildAsset } from '../src/helpers.js'
import flowEntry from '../src/index.js'

const babelOpts = {
Expand Down Expand Up @@ -99,7 +99,7 @@ describe('rollup-plugin-flow-entry', function() {
return rollup({
input: ['test/demo/entry1.js', 'test/demo/entry2.js'],
plugins: [
flowEntry({ types: 'test/types/entry.js.flow' }),
flowEntry({ types: 'test/demo/types/entry.js.flow' }),
babel(babelOpts)
]
})
Expand All @@ -109,12 +109,14 @@ describe('rollup-plugin-flow-entry', function() {
expect(output).to.deep.include({
fileName: 'entry1.js.flow',
isAsset: true,
source: "// @flow\n\nexport * from '../types/entry.js.flow'\n"
source:
"// @flow\n\nexport { default } from '../demo/types/entry.js.flow'\nexport * from '../demo/types/entry.js.flow'\n"
})
expect(output).to.deep.include({
fileName: 'entry2.js.flow',
isAsset: true,
source: "// @flow\n\nexport * from '../types/entry.js.flow'\n"
source:
"// @flow\n\nexport { default } from '../demo/types/entry.js.flow'\nexport * from '../demo/types/entry.js.flow'\n"
})
})
})
Expand All @@ -125,7 +127,7 @@ describe('rollup-plugin-flow-entry', function() {
plugins: [
flowEntry({
types: {
'entry1.js': 'test/types/entry.js.flow',
'entry1.js': 'test/demo/types/entry.js.flow',
'entry2.js': false,
'entry3.js': 'test/types/imaginary.js'
}
Expand All @@ -139,7 +141,8 @@ describe('rollup-plugin-flow-entry', function() {
expect(output).to.deep.include({
fileName: 'entry1.js.flow',
isAsset: true,
source: "// @flow\n\nexport * from '../types/entry.js.flow'\n"
source:
"// @flow\n\nexport { default } from '../demo/types/entry.js.flow'\nexport * from '../demo/types/entry.js.flow'\n"
})
})
})
Expand Down Expand Up @@ -193,29 +196,23 @@ describe('rollup-plugin-flow-entry', function() {

describe('buildEntry', function() {
it('handles difficult paths', function() {
const expected = {
const entry = {
fileName: 'sub/index.js.flow',
input: [
'/home/someone/sub/bare.js',
'/home/someone/windows\\style.js',
"/home/some'quotes'in/here.js"
]
}

expect(buildAsset('/home/someone', entry, 'semi-strict')).deep.equals({
fileName: 'sub/index.js.flow',
isAsset: true,
source:
'// @flow semi-strict\n\n' +
"export * from './bare.js'\n" +
"export * from '../windows/style.js'\n" +
"export * from '../../some\\'quotes\\'in/here.js'\n"
}

const paths = [
'/home/someone/sub/bare.js',
'/home/someone/windows\\style.js',
"/home/some'quotes'in/here.js"
]

expect(
buildEntry(
{ mode: 'semi-strict' },
'/home/someone',
'sub/index.js',
paths
)
).deep.equals(expected)
})
})
})