Post TypeScript Compilation: Uncaught SyntaxError: Unexpected token { - javascript

Scenario
Whenever I run my application from index.html, referencing the main.js file, which has been compiled from Typescript using es6 as the target and ESNext as the module, I get this error within the browser:
Error
main.js:3 Uncaught SyntaxError: Unexpected token {
Main.ts
/// IMPORTS
//////////////////////////////////////////////////////////////////////
import { Player } from './classes/Player' <= Error Here!
import { printTable } from './helpers/Helpers'
/// MAIN COMMANDS
//////////////////////////////////////////////////////////////////////
let p1 = new Player('Bob', 'Hunter', 1, 10);
printTable(p1);
Concept
I appear to be importing my code improperly, and (for whatever reason) don't know why or can't figure it out. Everywhere I look, this is indicated as the method of importing exported code, classes, etc... When all code exists within a single file, all classes, interfaces, helpers, etc..., everything converts and runs dandy, except when I want to compartmentalize my code, and handle it this way.
Any guidance would surely be appreciate.
Cheers and thanks in advance!
Source Code

...and ESNext as the module, I get this error within the browser:
main.js:3 Uncaught SyntaxError: Unexpected token {
Your error here is that you are trying to use ES6 import syntax in a traditional script (<script src="index.js"></script>). Using ES6 import and export only works with modules, used in the browser with <script type="module" src="whatever"></script>.
Have you looked into using Webpack? It can automatically compile your TypeScript files into one JavaScript bundle that you can load into your HTML file with a single script tag.

For you to import in this way you are using, you must export the class reference directly, without being inside an object.
Examples:
(correct) export Player;
(wrong) export { Player }

Related

import inside Electron renderer script

I followed the Electron typescript quick-start code structure. Basically I cloned the repo.
It worked fine until I wanted to split my code in multiple .ts files, and import them in the renderer script.
Then I get the Uncaught ReferenceError: exports is not defined. Because of this line on top of the renderer.ts:
import { stuff } from "./otherfile.ts";
After digging for more info, it seems that the reason is the "module": "commonjs" from tsconfig... But if I change that to esnext then Electron will not load the preload script anymore!
Has anyone actually managed to get Electron and typescript fully working? I mean, with being able to use import across multiple files and stuff like that?
Minimal reproductible example:
file structure:
/dist
/dist/main.js
/dist/preload.js
/dist/renderer.js
/dist/stuff.js
/src
/src/main.ts
/src/preload.ts
/src/renderer.ts
/src/stuff.ts
/index.html
/src/main.ts:
import { ipcMain } from "electron"; // imports work fine in main
...
ipcMain.on(...
/src/preload.ts:
import { contextBridge, ipcRenderer} from "electron"; // imports work fine in preload
contextBridge.exposeInMainWorld("my-api", { ....
/src/renderer.ts
import stuff from "./stuff.ts"; // import fails in renderer (exports is not defined error)
/src/stuff.ts
const stuff = { ... };
export default stuff;
/index.html
<html>
...
<script src="./dist/renderer.js"></script>
</html>
ts.config:
if I change "module" to "es6", then preload.ts will not load.
if I leave "module" to "commonjs" then imports in renderer.ts not work
If I manually add var exports = {} in renderer.js after ts compiles the file, then I get a different error "require is not defined"
TL;DR
I believe your error is caused by confusing CommonJS (CJS) and ES modules (ESM). It sounds like you are using CJS exports with ESM imports which is not compatible. The next section is a working Election example and after that is a comparison of CJS and ESM exports (export) and require (import).
Working Solution
Your OP did not include any code so this is my best guess at what the issue was and provides a working solution. Start by setting up the code:
# Clone this repository
git clone https://github.com/electron/electron-quick-start-typescript
# Go into the repository
cd electron-quick-start-typescript
# Install dependencies
npm install
Next create the following file:
// src/otherfile.ts
export const stuff = {
fullOf: 'stuff'
}
export function shareLocation(loc: string): void {
console.log(`I was imported by: ${loc}`);
}
Now open the src/main.ts file and make the following changes:
// Add import to top of file.
import {stuff, shareLocation} from "./otherfile"
// Add this code to the end of the createWindow() function.
shareLocation('main.ts (main.js)');
Now if you run this example with npm start you will see I was imported by: main.ts (main.js) in your terminal.
If you tried to do this with the src/preload.ts file you will get an error because of sandboxing. To see this, make the following change in src/preload.ts:
// Add to first line.
import {stuff, shareLocation} from "./otherfile";
// Add after the for loop inside the DOMContentLoaded function.
shareLocation('preload.ts (preload.js)');
Now if run npm start you will get an error in the electron window (web browser) console. This is because of important security settings within Electron. Make the following change to your src/main.ts file:
// Modify the webPreferences of the new BrowserWindow.
const mainWindow = new BrowserWindow({
height: 600,
webPreferences: {
preload: path.join(__dirname, "preload.js"),
sandbox: false // <=== ADD THIS LINE
},
width: 800,
});
Your import will work as intended now and you should see I was imported by: preload.ts (preload.js) in the electron window (web browser) console. Keep in mind this code is insecure! You should use IPC instead of disabling the sandbox.
If you are still getting an error I believe it is because your confusing CommonJS (CJS) and ES modules (ESM). It sounds like you are using CJS exports with ESM imports. See the next section that demos the difference.
CommonJS (CJS) vs ES modules (ESM)
CommonJS (CJS)
CommonJS was the original method for exporting and requiring (importing) modules in Node. It was a standard introduced by Mozilla engineer Kevin Dangoor in 2009. You would write some code in one file like so:
// example.js
function hello() {
console.log('World!');
}
module.exports = hello;
And then require (import) the code into another module/file like so:
// main.js
const hello = require('./example.js');
hello();
Just like ES modules, which is a newer standard, "you can export functions, var, let, const, and classes" (MDN Docs). Here is an bigger example that exports two functions using a single export object:
// example.js
function hello() {
console.log('World!');
}
function foo() {
console.log('Bar!');
}
module.exports = {
hello,
foo
};
We could even change the exported object to refer to our functions by a different name. We could change the export code to this for example:
module.exports = {
H: hello,
F: foo
};
We would then require (import) this code like so:
// main.js
const {hello, foo} = require('./example.js');
hello();
foo();
// OR with the other export example
const {H, F} = require('./example.js');
H();
F();
ES Module (ESM)
This is the modern approach to including JavaScript code from one file/module into another, and is designed to work in modern browsers. If you were manually writing JavaScript modules for the web you would have to add the type="module" attribute to the script tag that loads your module.
In your setup, and many others, you have bundlers (webpack for example) of some kind that handle this for you by compiling/transpiling your code for you. This topic is out of scope to this question though, so lets look at an example.
Using ESM our simple example now becomes:
// example.js
function hello() {
console.log('World!');
}
export default hello;
Notice how our export statement has changed. To keep things simple I provide a default export when exporting a single item. We can also inline the export:
// example.js
export default function hello() {
console.log('World!');
}
And then we import the code into another module/file like so:
// main.js
import hello from "example";
hello();
If you do not want to use default in your exported module you will have to use a different syntax for importing your code. As Darryl Noakes mentions, that would be Named Exports. Since this is a TypeScript project this includes changing your TypeScript config. This SO answer covers what to do if you want to go this route with this project.
If you export multiple items you do not have to use the default statement:
// example.js
function hello() {
console.log('World!');
}
function foo() {
console.log('Bar!');
}
export {
hello,
foo
}
And you import the code in a similar fashion to object destructing:
// main.js
import {hello, foo} from "example";
hello();
foo();
For additional help with import this MDN Doc is a great start as well as this Free Code Camp Article. JavaScript Tutorial has a great tutorial on what object destructing is. This SO Q&A goes more into module imports including why they are not actually using destructing. Thanks again to Darryl Noakes for pointing that out!
Sorry, it's not possible the way that boilerplate is configured.
To use import/require inside of the renderer process, you must either lower the default security settings in the latest versions of Electron OR use a transpiler so that the final code the renderer executes will not include import/require.
And in the electron-quick-start-typescript project you'll find this file that confirms that as the issue.
// This file is required by the index.html file and will
// be executed in the renderer process for that window.
// No Node.js APIs are available in this process unless
// nodeIntegration is set to true in webPreferences.
// Use preload.js to selectively enable features
// needed in the renderer process.
Node.js APIs include import and require.
Furthermore, when you do access require, if sandbox is enabled then you're actually getting a polyfilled version, not the underlying require - see these notes about preload script sandboxing
Your only other option short of changing boilerplates is to access anything you need via the preload script like window.api.yourFunction instead.
You may wish to use a boilerplate which includes transpilation via Webpack to enable you to use import inside of your renderer process.
I believe both of these boilerplates accommodate it (and I can probably advise you somewhat if you get stuck with one of them)
https://github.com/electron-react-boilerplate/electron-react-boilerplate (I recommend this one)
https://github.com/reZach/secure-electron-template
PS - If you're writing code intended for distribution to others, please do not enable nodeIntegration as it breaks the security model of Electron!
PPS - You can also try "module": "ES2020", which is what we use - but it will not fix your issue of not being able to import/require within the renderer process.
Electron doesn't fully support ECMAScript modules yet, but this should not impact your Typescript code, which can be very modern.
OPTION 1 (LOOSE FILES)
Do this in your index.html to bootstrap your renderer process's Typescript code, as in this initial sample of mine. This will get you up and running quickly, though it uses node integration, which should be disabled before you release to production:
<body>
<div id='root' class='container'></div>
<script type='text/javascript'>
require('./built/renderer');
</script>
</body>
OPTION 2 (BUNDLED)
To get rid of the require statement, and operate closer to an SPA, in line with Electron security recommendations, you can use webpack bundling and reference built Typescript as follows, as in this updated sample of mine:
<body>
<div id='root' class='container'></div>
<script type='module' src='vendor.bundle.js'></script>
<script type='module' src='app.bundle.js'></script>
</body>
SETTINGS
I specify type=commonjs in package.json and module=commonjs in the tsconfig.json file, whereas in non-Electron projects I use ECMAScript module settings instead, to build slightly better output.
The art of it of course is to produce a modern code setup that you can grow according to your own preferences, rather than being limited to what starter projects do.

Having trouble converting this require statement into an import statement

I have a require statement in my script.
const packageJsonVersion = require('../package.json').version;
If I try and run the script I get an error saying I need to convert it to an import statement.
I changed the code to import { version as packageJsonVersion } from '../package.json' but when I run the script I get the following error.
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".json" for /home/alex/_code/connect/package.json
I'm not sure how to get around this.
It depends on what environment you're doing this in.
Right now, browsers don't natively support JSON modules (though it's coming).
If you're doing this with a bundler, you need to be sure to tell the bundler how to handle the .json extension.
If you're doing this in Node.js, you need the JSON module notation (assert {type: "json"}).
Separately, though, JSON modules won't support named exports (this is mentioned by both links above). You'll have to import the default and then use the version property on it:
import data from "../package.json" assert { type: "json" };
const packageJsonVersion = data.version;

Typescript compile with imports

So I have two files: a.ts and b.ts. b exports something for a to use. The TypeScript compiler handles this fine, but refuses to create valid output for a browser. Example:
import { test } from "./b";
console.log(test);
b.ts
export const test = "quest";
and now I try compiling it:
$ tsc --lib dom,es2018 --target ES2018 --out test.js a.ts b.ts
b.ts:1:1 - error TS6131: Cannot compile modules using option 'out' unless the '--module' flag is 'amd' or 'system'.
1 export const test = "quest";
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Found 1 error.
hm, weird. Okay, let's try using --module. If I use amd a browser that loads test.js will tell me: ReferenceError: define is not defined. If I try system it says: ReferenceError: System is not defined.
Okay, that's not working. Let's try without setting a single output file with --out. That gives me:
SyntaxError: import declarations may only appear at top level of a module a.js:1
SyntaxError: export declarations may only appear at top level of a module b.js:1
So based on this, it seems like my only options for using typescript are:
Put all code in a single file
Use an external library like require.js or webpack
Learn what a module is and if/how it can be used in a browser
Or am I missing something? How do I compile Typescript using imports without adding libraries/modules ?
TypeScript version: 3.8.3
Syntax error
please correct this
export const test = "quest";
to
export test = "quest";
No need to add any const here while exporting

JavaScript/Node js import and export module(Using exported node Js module in JavaScript file)

I am trying to export node js modules to use in JavaScript. This is what I have done.
-Installed esm in node.
in my node file I did like this:
require = require("esm")(module/*, options*/);
export function Myjs(name, surname){console.log(name, surname)};
This does not give error.
In my js file I did:
1- import { Myjs } from '/javascripts/sockets/bases.js'; This gives error as "Uncaught SyntaxError: Cannot use import statement outside a module". I have been reading topics regarding this error, and it suggests that I should include "type: module" while including the file. But I do not include node js files as scripts on html.??
2- I try to import dynamically like this:
import('/javascripts/sockets/bases.js')
.then((module) => {
module.Myjs("bola", "carro");
});
This gives error if detects require() in the file and it gives error "require is not defined" or any node js module that js does not recognize.??
What I'm trying to achieve is: Based on an event on js file trigger the function (Myjs()) I'm trying to import. How could achieve this? I tried babel and I wasn't successful. Thanks

Why importing * from a file execute the code?

Why exporting nothing from a file (or module), renamed it and just put the variable in the scope execute the code?
// log.js
console.log('A message');
// index.js
import * as app from 'log.js';
app;
Result: A message gets printed, as expected. But if you comment out the app line:
// log.js
console.log('A message');
// index.js
import * as app from 'log.js';
// app;
Nothing gets printed. What's going on here?
There is an example using codesandbox: https://codesandbox.io/s/xenodochial-keldysh-j0sli
Update: I did't realize it was the typescript example I added to the question, I was doing test using typescript and javascript: here's the codesandbox for the js version: https://codesandbox.io/s/silly-oskar-0mo2o
This is expected Typescript behavior. See Why are imports being elided in my emit? and this thread on Typescript's github.
TypeScript assumes that module imports do not have side effects, so it removes module imports that aren't used in any expression.
Without the app namespace being used, Typescript doesn't even bother trying to import and run log.js because the namespace isn't being used anywhere and it assumes the module has no side effects, so there's no point importing it.
If you do want to assert that importing alone should have side effects, use:
import 'log.ts';
This results in the top-level code of log.ts being logged as expected.
Note that this is a Typescript-specific behavior. outside of Typescript, top-level code of a module does run whenever the module is imported, regardless of what was imported or referenced later.

Categories

Resources