I am writing a program in TypeScript and compiling code to JavaScript before running it on a browser. This is my package.json scripts:
"scripts": {
"clean": "rm -rf dist/",
// gsap.min.js is under ts/src/lib/
"build": "tsc && copyfiles -u 1 ts/src/lib/* dist/"
},
However, I cannot reference gsap in a TypeScript file as I get the the error Cannot find name 'gsap'. I believe this is because tsc does not read minified files (even if allowJs flag is true).
I can, however, use gsap fine in a JavaScript file, since the checking is less strict. (In JavaScript, referencing a variable that is not defined only throws an error when run, e.g. in browser console.)
Attempt to fix
I tried defining gsap, hoping gsap.min.js will then override the variable. However, gsap remains undefined at runtime (at least tsc compiles this with no errors). So this does not work.
<!-- index.html snippet -->
<script src="dist/src/lib/pseudo.js"></script>
<script src="dist/src/lib/gsap.min.js"></script>
// pseudo.js
let gsap: any;
If you use a bundler, then if you install gsap (npm install gsap) rather than just copying a minified version of it into your project, it should resolve the problem has gsap provides type definitions.
If you're not using a bundler, copy the .d.ts files containing the types (from https://github.com/greensock/GSAP/tree/master/types) into your project's directory tree, and if TypeScript doesn't automatically pick them up, point it at them via tsconfig.json. The type files include the necessary declaration to tell TypeScript about the gsap global (specifically, that declaration is in gsap-core.d.ts).
Related
I am trying to use react-vis library in my project. In their readme file, they have asked to import library by adding following lines in non-node environment:
If you're working in a non-node environment, you can also directly include the bundle and compiled style using basic html tags.
<link rel="stylesheet" href="https://unpkg.com/react-vis/dist/style.css">
<script type="text/javascript" src="https://unpkg.com/react-vis/dist/dist.min.js"></script>
The global reactVis object will now be available for you to play around.
When I did that, I got following error:
Uncaught TypeError: Cannot call a class as a function
at _classCallCheck (dist.min.js:formatted:23789:27)
at LabelSeries (dist.min.js:formatted:23823:21)
at Constructor.render (<anonymous>:1866:23)
at Constructor.<anonymous> (react.js:6029:34)
at Constructor._renderValidatedComponent (react.js:11403:21)
at Constructor.<anonymous> (react.js:5582:14)
at Constructor.mountComponent (react.js:11403:21)
at Constructor.mountChildren (react.js:10913:42)
at Constructor._createContentMarkup (react.js:6812:32)
at Constructor.<anonymous> (react.js:6734:14)
After a lot of digging, I realised the issue was that the react-vis library follows ES6, while my project is in ES5. So, I thought to transpile react-vis library to ES5. I used babel as explained in this post to transpile ES6 to ES5 (with slight changes). But, it generated multiple files. I needed single bundle file.
So, I followed steps explained here to generate single ES5 bundle file. I used same webpack.config.js as specified in these steps. And my scripts in package.json look something like this:
"build:babel": "babel src -d build"
"build:app": "npm run build:babel",
"build:bundle": "webpack --mode production",
"build3": "npm run build:app && npm run build:bundle"
It was successful, but when I used generated bundle file, I got error ReferenceError: reactVis. When I checked the generated bundle file, it did not contain reactVis variable. So, now I have two doubts:
Q1. how can I tell webpack to preserve reactVis variable?
Q2. how can I make webpack to add it to global scope (or how can I access this variable in my project once I linked the generated bundle)?
There is a 3rd party all-javascript npm scoped package, let's call it #foo, with a module inside called bar. I wanted to use the react component #foo/bar/X from within my typescript .tsx file. I immediately ran into "module not found" when I tried to import X from '#foo/bar/X'. How can I resolve this using #types typescript to resolve the module X and get webpack to run without errors?
My starting point was this introduction on how to how to use react and webpack with typescript: https://www.typescriptlang.org/docs/handbook/react-&-webpack.html. However, said instructions do not tackle the problem that the react component I want to use has no #types. So my first step is I needed to add #types.
There was one important nuance about the scoped module #foo/bar, which was that it broke all its subcomponents into individual sub modules like #foo/bar/X, #foo/bar/Y, and #foo/bar/Z with #foo/bar itself having no functionality. As we will see that was an important nuance which made solving this a bit trickier than if I had not had to import a scoped module into my typescript.
There is a nice blog post here about how to import a vanilla javascript into typescript. Unfortunately there is a shortcoming to the proposed approach. Namely, the idea of adding "index.d.ts' to a src/#typings has two problems:
It works only for running the typescript compiler (tsc) locally. That is to say it will make tsc happy, by allowing tsc to resolve the module from your your local ".d.ts" file, but webpack will still fail with module not found.
The proposed approach didn't teach me how to deal with scoped modules which have different rules for how folders under #types are named
Whatever approach I tried, I knew I needed to have this import statement in my typescript file:
import X from '#foo/bar/X';
ReactDOM.render(
<X/>,
document.getElementById("example")
);
Somewhere, I needed to have the following in a ".d.ts" declarations file like index.d.ts, that much was obvious:
declare module '#foo/bar';
declare module '#foo/bar/X';
But running 'webpack' yielding Module not found: Error: Can't resolve '#foo/bar/X'
I decided to run the typescript compiler (tsc) with this flag to see the possible places where tsc would try to resolve the module.
tsc --traceresolution
Using the traceresolution flag I was able to see something very interesting: that the location under node_modules/#types where tsc searches was quite unexepected (and thereby where webpack searches, since webpack follows the same rules as tsc). It was expecting to find my index.d.ts file under node_modules/#types/foo__bar NOTE THE DOUBLE UNDERSCORE. That's right, for scope packages, you cannot have node_modules/#types/#foo/bar/index.d.ts. Instead you must use a single folder under #types named foo__bar with DOUBLE UNDERSCORE.
With this knowledge I created #types/foo__bar. I copied package.json from the #foo/bar module into #types/foo__bar. I cleaned out all the scripts and other useless stuff that the #types would not need, and stripped it down to this:
{
"name": "#types/#sfoo/bar",
"version": "2.6.0",
"peerDependencies": {
"react": "^16.3",
"react-dom": "^16",
"styled-components": "^3"
},
"dependencies": {
...
},
"engines": {
"node": ">=6"
},
"gitHead": "ac0288aaa47a4f15e56db3a5eff4424fb7905419",
"main": "",
"types": "index"
}
But yet there was one more problem! Even after tsc could resolve my #types, webpack still yielded module not found: can't resolve #foo/bar. So why the heck can't webpack find it? Again, I found a flag that could show me more:
webpack --verbose
But here I had less luck since the webpack rules were just like the typescript rules for module resolution. However from the --verbose output I did notice that one file webpack was interested in was node_modules/#foo/bar/index.js. However, the bar module lacked any index.js since it was really just an empty enclosing module around submodules like #foo/bar/X. Whether this is a bug or a feature of webpack, I don't know, but ADDING AN EMPTY index.js to node_modules/#foo/bar pacified webpack, and my simple example now worked with component X displayed in the browser when I loaded the HTML page.
I have a typescript project that uses paths for imports. For example:
"paths": {
"#example/*": ["./src/*"],
}
Thus the project can import files directly from using statement like:
import { foo } from "#example/boo/foo";
For publishing to NPM I have I'm compiling the typescript files and then copying the result to a dist folder. Thus all the *.d.ts and corresponding *.js files are in the dist folder. I also copy package.json to the dist folder.
I now test this by generation a new typescript project and then run npm i -S ../example/dist, in order to install the project and attempt to run some of the compiled typescript code.
However the relative imports no longer work. For example if boo.ts depends on foo.ts it will say that it can't resolve foo.ts.
When I look at the *.d.ts files they contain the same paths that were used the source code before it was compiled. Is it possible to turn these into relative paths?
Update
I looks as if generating relative paths for Node is something Typescript does not perform automatically. If you would like this feature, as I would, please provide feedback on this bug report.
As a brief follow-up to arhnee's suggestion, it seems that as of Aug 2020, Microsoft still refuses to implement custom transformers for whatever reason, so these modules remain relevant.
So to future readers, here's how you can actually compile TS path aliases to relative paths. ttypescript is merely a transformer framework that requires a "path transformer" in order to actually convert the TS path aliases. Thus you will need to install both ttypescript and typescript-transform-paths.
npm i --save ttypescript typescript-transform-paths
Then, it's easy as just specifying usage by adding the following property to the compilerOptions object in tsconfig.json:
"plugins": [
{ "transform": "typescript-transform-paths" }
]
And finally, run ttsc instead of tsc.
There is a project called ttypescript that you can use for this. If you use it with the module typescript-transform-paths I beleive it will acheive what you want.
I have a JS file that imports a module:
import { LitElement, html } from '../lib/#polymer/lit-element/lit-element.js';
This causes a load of TypeScript errors, some due to versions, some due to a test framework used by some of the modules, some due to different noImplicitAny or strictNullChecks settings, but most due to Yarn flattening the npm hierarchy (and not rewriting internal references).
None of these are errors in my code, and I'm not even referencing the TS directly - most are coming from the TS source for that JS file.
How do I stop these from breaking my build? I don't care whether the source or test TS files compile in place, I only need the result JS file.
Even in TS files I don't want TSC to recompile the referenced Yarn modules - at most it should use them for type checking (and even then prefer a *.d.ts file over the source).
I've tried --noResolve and that doesn't fix it (and also breaks the es2017 lib references).
I'm setting up AVA with Typescript to tests my JS code. Internally, I'm first calling TSC to compile my files, then call AVA with babel-register to test them (Babel register allowing require to be resolved).
"ava": "tsc && ava testJs/**/*.spec.js"
testJs being the output folder of Ts. My problem is that, even thought everything pretty much work, I have this kind of statement, usually picked up by Webpack :
import "./index.page.css";
Webpack gently require it, but babel-register doesn't. I had to change the behavior to accept .css file as noop. Here is the issue : because I'm using tsc as a compiler, those files are not copied at all in testJs, meaning they are not available in the first place.
I wanted to know what would be the best way to solve this, as I think copy-pasting the whole folder (to have all files available) just to execute tests is a bit of an overkill. Especially since, if I suddenly import a .json file (for example)I will have new problems.
For example, is there a way to tell babel-register to ignore require it cant resolve instead of breaking ?
Thanks !
You can use ignore-styles to ignore certain types of requires. By default it ignores all kinds of CSS and images (full list) and you can customise it to ignore other extensions as well.
You simply require it before babel-register in your AVA config.