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

SyntaxError: Unexpected token 'export' #28

Closed
ericshuk opened this issue Jan 6, 2022 · 14 comments
Closed

SyntaxError: Unexpected token 'export' #28

ericshuk opened this issue Jan 6, 2022 · 14 comments

Comments

@ericshuk
Copy link

ericshuk commented Jan 6, 2022

Describe the bug
I get the following error when trying to import the package like so import { downloadZip } from "client-zip";:

SyntaxError: Unexpected token 'export'
    at compileFunction (<anonymous>)
    at Object.compileFunction (node:vm:352:18)
    at wrapSafe (node:internal/modules/cjs/loader:1031:15)
    at Module._compile (node:internal/modules/cjs/loader:1065:27)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Module.require (node:internal/modules/cjs/loader:1005:19)
    at require (node:internal/modules/cjs/helpers:94:18)
    at Object.client-zip (/Users/ericamshukov/Projects/dt_bridge_pipetrekker_spa/.next/server/pages/index.js:20570:18) {
  page: '/'
}

To Reproduce
Steps to reproduce the behavior:

  1. Import in TypeScript file
  2. npm run
  3. SyntaxError: Unexpected token 'export'

Additional context
I am developing a project in NextJS and TypeScript, but everything else is working properly.

@ericshuk ericshuk changed the title error - /Users/ericamshukov/Projects/dt_bridge_pipetrekker_spa/node_modules/client-zip/index.js:1 SyntaxError: Unexpected token 'export' Jan 6, 2022
@Touffy
Copy link
Owner

Touffy commented Jan 7, 2022

Hi. client-zip is meant to be used in a browser (there are faster alternatives in Node.js) and is therefore released as an ES6 module only. Node.js supports the ES6 module syntax, but you have to ask nicely and you'll also be missing a few Web API globals that client-zip rely on.

@Touffy
Copy link
Owner

Touffy commented Jan 7, 2022

My point is, I suspect your project is configured to only understand CommonJS imports and probably also to output ES5-compatible code (a shame, really, but those big projects tend to value backwards compatibility way too much…). One way around it would be to exclude the client-zip import from bundling — import it only at runtime in the browser.

@Touffy
Copy link
Owner

Touffy commented Jan 20, 2022

Any luck fixing your module problem, @FullyFerret ? it's a bit off-topic, but it could still be useful to other users with a similar setup if you explained how you fixed it before closing the issue.

@venkatd
Copy link

venkatd commented Jan 21, 2022

@Touffy I was helping a friend debug something similar

Unfortunately with nextjs the solution isn't simple. There are two things that can be done in nextjs to use this:

You can use a nextjs dynamic component which is only rendered client side:

const FileAttachmentLink = dynamic<Attachment>(
  () => import('./file-attachment-link').then((m) => m.FileAttachmentLink),
  {
    ssr: false
  }
)

Then, in that component, you can use a dynamic import to avoid it being included on the server. For example in a callback, you could do the following:

export function DownlaodFileLink() {
  async function onDownload() {
    // import the library dynamically at runtime
    const { downloadZip } = await import('client-zip')

    const code = await fetch(
      'https://raw.githubusercontent.com/Touffy/client-zip/master/src/index.ts'
    )
    const intro = {
      name: 'intro.txt',
      lastModified: new Date(),
      input: 'Hello. This is the client-zip library.'
    }

    // get the ZIP stream in a Blob
    const blob = await downloadZip([intro, code]).blob()

    // make and click a temporary link to download the Blob
    const link = document.createElement('a')
    link.href = URL.createObjectURL(blob)
    link.download = 'test.zip'
    link.click()
    link.remove()
  }

  return (
    <a onClick={onDownload}>
      {filename}
    </a>
  )
}

@Touffy
Copy link
Owner

Touffy commented Jan 21, 2022

Right. I was afraid you'd have to do something like this. Obviously we don't want client-zip on the server-side. It won't work, and it's not like Next would understand "server-side rendering" the Zip file anyway even if it could parse the code.

This method also has a little performance drawback because you import client-zip just when you need to create the Zip file (the first time, anyway), so it will add a bit of latency. That can be fixed easily by moving the import() call outside the onDownload function so it's started as soon as you render the component, storing just the Promise, and awaiting that Promise in onDownload.

@Touffy Touffy closed this as completed Jan 23, 2022
@wildky
Copy link

wildky commented Apr 9, 2022

Thanks @venkatd for the code example.

@Touffy thanks for the good work on this package. I came hunting for this question. It may seem silly given the package name is literally "client-zip" but it wasn't immediately evident to me that this only works in a client environment. I was also trying to integrate with nextJS. I imagine other folks will try to do similar things given the popularity of frameworks like nextjs right now, maybe it is worth elevating the discussion of using this package with SSR to the docs somewhere?

@Touffy
Copy link
Owner

Touffy commented Apr 12, 2022

I'll try to find some room for it along with the CSP tips in the next minor update.

@Touffy
Copy link
Owner

Touffy commented Apr 15, 2022

Forgot to do it before the update… oh well, at least it's on GitHub now, will be pushed to NPM along with the next version… probably when I complete Content-Length prediction.

@net-tech
Copy link

net-tech commented Nov 9, 2022

(there are faster alternatives in Node.js)
@Touffy could you list some alternatives?

@Touffy
Copy link
Owner

Touffy commented Nov 9, 2022

The faster alternative in Node is to call a (fast) native Zip program. I am not an expert on those, and I'm sure there are already performance reviews you can find elsewhere. I don't mean to be rude, I just don't want to give you poor advice from a lack of research, and I don't see why I should do the research. Zipping on the server-side is off-topic for this project. It happens to work in Deno, yes, but that wasn't even intended.

However, there is a general explanation I can give you. Native programs can use CPU features that are not available to client-zip (because they're not in JavaScript or WebAssembly), such as a hardware CRC32 primitive, and 256-bit vector operations. Also, Node and native programs have efficient random access to the local filesystem, which means they don't have to use streaming mode if zipping from there, and they can copy data from one part of the filesystem to another with efficient kernel calls instead of moving it through userspace buffers. As you can see on my benchmark, the native Zip program I used is much faster than client-zip and other JavaScript alternatives. However, I can't say if it's the fastest, or the best suited for integration with Node.

@seladb
Copy link

seladb commented Mar 7, 2024

@Touffy I hit the same issue while running tests with jest. Do you have any workaround in mind for using jest with React components that use client-zip?

@Touffy
Copy link
Owner

Touffy commented Mar 7, 2024

The problem is not React or jest, it's their configuration. You can either make them understand ESM imports, or skip the whole thing by making the import dynamic at runtime. The details will depend on your existing setup.

@seladb
Copy link

seladb commented Mar 8, 2024

I just realized that for tests I can probably mock client-zip because I don't really need to test the library iteslf

@Touffy
Copy link
Owner

Touffy commented Mar 8, 2024

That's right. You probably don't need to run downloadZip at all as part of your test (so you can use an empty mock) but even if you have to, you can easily mock downloadZip with a one-liner that returns new Response(new Blob()).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants