parcel-bundler / parcel

The zero configuration build tool for the web. ๐Ÿ“ฆ๐Ÿš€

Home Page:https://parceljs.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Inline file in bundle with fs.readFileSync not working with TypeScript

alexharri opened this issue ยท comments

๐Ÿ› bug report

Parcel provides inline asset importing via statically analyzable fs.readFileSync statements. They work as expected in JavaScript but not Typescript.

๐ŸŽ› Configuration (.babelrc, package.json, cli command)

package.json

{
  "scripts": {
    "start": "parcel index.html"
  },
  "dependencies": {
    "parcel": "^1.9.6"
  },
  "devDependencies": {
    "@types/node": "^10.5.2",
    "typescript": "^2.9.2"
  }
}

No custom .babelrc file

๐Ÿค” Expected Behavior

A statically analyzable fs.readFileSync statement should inline the file in the bundle, both in .js and .ts files.

๐Ÿ˜ฏ Current Behavior

fs.readFileSync statements are inlined in .js files but aren't in .ts files.

Output from console (see code sample)

Console output

index.ts:5 Uncaught TypeError: fs_1.default.readFileSync is not a function
    at Object.parcelRequire.index.ts.fs (index.ts:5)
    at newRequire (2.0d1d60a5.js:48)
    at parcelRequire...\node_modules\parcel\src\builtins\_empty.js (2.0d1d60a5.js:80)
    at 2.0d1d60a5.js:106
parcelRequire.index.ts.fs @ index.ts:5
newRequire @ 2.0d1d60a5.js:48
parcelRequire...\node_modules\parcel\src\builtins\_empty.js @ 2.0d1d60a5.js:80
(anonymous) @ 2.0d1d60a5.js:106

๐Ÿ’ Possible Solution

Implement inline file imports in .ts files.

๐Ÿ”ฆ Context

I was working with WebGL and writing shaders. Writing them with template strings didn't give me any syntax highlighting so I wanted to write them as .frag and .vert files. I expected to be able to import them easily with Parcel but received the above error. After a bit of head-scratching I realized it was a Parcel + TypeScript problem and made a repro.

There's a fairly simple workaround this issue, which is just creating a .js file which imports the desired file, for example.

// index.ts

import textFile from "./textFile.js";

// Do stuff with the text file.
// textFile.js

import fs from "fs";

const textFile = fs.readFileSync(__dirname + "/textFile.txt", "utf8");

export default textFile;

It works but could create a lot of noise if using a lot of inline assets.

๐Ÿ’ป Code Sample

Repo with code to reproduce the issue.

๐ŸŒ Your Environment

Software Version(s)
Parcel 1.9.6
Node 8.9.1
npm/Yarn npm 6.1.0
Operating System Windows 7

I'm having the same problem. Has anybody found a temporary workaround?

@ishaantaylor I showed a possible workaround in the Context section of the issue, should work if allowJs is enabled in the tsconfig.

async transform() {
if (this.options.target === 'browser') {
if (this.dependencies.has('fs') && FS_RE.test(this.contents)) {
// Check if we should ignore fs calls
// See https://github.com/defunctzombie/node-browser-resolve#skip
let pkg = await this.getPackage();
let ignore = pkg && pkg.browser && pkg.browser.fs === false;
if (!ignore) {
await this.parseIfNeeded();
this.traverse(fsVisitor);
}

There is a regex in JSAsset that launches FSVisitor.
So my intuition (I'm researching it for like 5 minutes, so I might be wrong) is we should add transform method override to TSAsset and launch FSVisitor from there.

Disregard what's above.
I've added test(#2046) to my fork of parcel, got really surprised that it works and made a reproduction repo.

Named import works fine for me and all of the imports work on CodeSandbox ๐Ÿ˜ฎ .

import { readFileSync } from 'fs';
const raw = readFileSync(__dirname + '/raw.tsx', 'utf-8');

Is there another issue to support transforming this readFileSync when not using named imports?

Is weird that we can do fs.readFileSync() in .js files but not in .ts/.tsx files.

Hello Guys,
I am facing a similar problem
I have imported "fs" using import * as fs from "fs" and trying to execute fs.readFile() and fs.readFileSync with there respective arguments.
But, getting an error as "fs" object doesn't have a function readFile() and readFileSync().
I have tried using var fsFile = require("fs");.

I am using in Cypress automation project in typescript.

I'm running into this issue trying to use the adl-xapiwrapper library with Typescript, using import ADL from 'adl-xapiwrapper'. The actual fs.readFileSync call is several layers of require statements down from adl-xapiwrapper, in mime.js. Here's the callstack where I get the fs.readFileSync is not a function error. This is parcel version 1.11.0

Mime.load (mime.js:54)
parcelRequire.../node_modules/mime/mime.js.path (mime.js:90)
newRequire (src.f69400ca.js:49)
localRequire (src.f69400ca.js:55)
parcelRequire.../node_modules/request/request.js.http (request.js:16)
newRequire (src.f69400ca.js:49)
localRequire (src.f69400ca.js:55)
parcelRequire.../node_modules/request/index.js.cookie-jar (index.js:20)
newRequire (src.f69400ca.js:49)
localRequire (src.f69400ca.js:55)
parcelRequire.../node_modules/adl-xapiwrapper/lib/adl-xapiwrapper.js.request (adl-xapiwrapper.js:11)
newRequire (src.f69400ca.js:49)
localRequire (src.f69400ca.js:55)
parcelRequire.index.tsx.react (index.tsx:11)
newRequire (src.f69400ca.js:49)
(anonymous) (src.f69400ca.js:81)
(anonymous) (src.f69400ca.js:107)

Edit: not quite sure this is the same bug, I'm actually getting this just with a plain js file trying to import adl-xapiwrapper, perhaps I'll make a separate issue for it.

I have the same problem, even with named imports it does not get inlined.

Input:

export async function setSchema(dgraphClient: DgraphClient): Promise<void> {
    // This gets bundled by Parcel
    const schema = readFileSync(__dirname + '/../dgraph.schema', 'utf-8')
    const op = new Operation()
    op.setSchema(schema)
    await dgraphClient.alter(op)
}

Output:

async function setSchema(dgraphClient) {
  // This gets bundled by Parcel
  const schema = fs_1.readFileSync(__dirname + '/../dgraph.schema', 'utf-8');
  const op = new dgraph_js_1.Operation();
  op.setSchema(schema);
  await dgraphClient.alter(op);
}

@felixfbecker

I have the same problem, even with named imports it does not get inlined.

Could you please share your tsconfig or a complete reproduction?
Works for me with this typescript:

import { readFileSync } from "fs";

export function setSchema() {
	const schema = readFileSync(__dirname + "/test.schema", "utf-8");
	console.log(schema);
}

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs.

Working with Parcel 2.4.1 and this seems like it's still happening. Would it make sense to reopen the issue?

Can someone reopen this? How does it get closed?

@bibek-stripe @spion Can you provide a reproduction? When I tried it in #1736 (comment), it worked fine

Sorry, never mind - looks like this is a different issue - its the use of template strings that doesn't work:

fs.readFileSync(`${__dirname}/path/to/file.txt`)

is simply not recognized.

In Parcel 2, the currently recommend way to read a file as literal, plain text is using the bundle-text: scheme, as in:

import text from 'bundle-text:./myFile';
console.log(text);

Hello!๐Ÿ‘‹

First, I encountered the same exact issue; fs.writeFile doesn't work within .ts files.

Then, I update my package.json just using:

{
  "source": "src/create-projects.ts",
  "main": "dist/create-projects.js",
  "type": "module",
}

And voilร  ! I can parcel build and run a node dist/create-projects.js to create my file.

I hope this can help you.