I have structure project like this.
- project folder
-- b.js
-- server (folder nestjs src)
I fix nest-cli.json
{
"collection": "#nestjs/schematics",
"sourceRoot": "server"
}
I want to use file b in my main.ts file
import * as a from '../b.js';
I get an error:
Error: Cannot find module '../b.js'
I add this file to nest-cli.json
{
"collection": "#nestjs/schematics",
"sourceRoot": "server",
"compilerOptions": {
"assets": [
"b.js"
]
}
Now this file add to directory, which I set "outDir": "./dist/server",
But the error has not disappeared
Sometimes you have to test out . and ...
. gives you the current directory files. So ./b.js could work.
.. gives you directory above the current directory. So it thinks you are at ../b.js, which doesn't exist.
If /home/file/ was your project directory, and including index.js in app.js then using ../index.js would mean /home/index.js.
However, if you use ./index.js, you would get /home/file/index.js.
That's the difference between . and ...
Related
I want to publish a typescript npm package, with types embedded.
My folder structure is as follows
dist/
[...see below]
src/
global.d.ts
index.ts
otherfile.ts
test/
examples/
For the sake of an example, my global.d.ts file includes typings relevant to the whole project.
My index.d.ts file uses these types, and exports functions.
//global.d.ts
interface Dog {
name: string,
age: number
}
//more types...
//index.ts
import {func1, func2} from './otherfile.ts'
export default function getDogName(dog: Dog): string {
return dog.name
}
export {func1, func2}
I can build this code, and it all runs correctly, so typescript knows about global.d.ts. Yet, when I run tsc (using the below config), it generates files into my dist/ folder, which doesnt include the global.d.ts file. ie: dist only contains index.d.ts and otherfile.d.ts. Shouldn't dist/ also contain global.d.ts? If not - how will people who install my package know the types for Dog?
My config looks like this for build time.
//tsconfig.json
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"outDir": "dist/",
"declaration": true
},
// Only point to typings and the start of your source, e.g. `src/index.ts`
"include": [ "src/*"],
"exclude": ["node_modules", "examples", "test"]
}
For reference, the actual repo is here: https://github.com/bhaviksingh/lsystem
Alright :) After much experimentation, I wanted to share what I did.
There are potentially better solutions (would love to find them), but the TLDR is that I changed it from a global declaration file, to a regular TS module, and exported/imported all the types.
So I renamed the global.d.ts file, to just be interfaces.ts, and then exported the type.
//interfaces.ts (renamed from global.d.ts)
export interface Dog {
name: string,
age: number
}
//more types...
I imported this type into index.d.ts and re-exported it
//index.ts
import Dog from "./interfaces"
export {Dog}
TSConfig knows to compile these declarations
//tsconfig.global.json
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"outDir": "dist/",
"declaration": true
},
// Only point to typings and the start of your source, e.g. `src/index.ts`
"include": [ "src/*"],
"exclude": ["node_modules", "examples/", "test/"]
}
and my package.json points to the compiled declaration file.
//package.json
{
//...
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
//...
"build": "tsc -p tsconfig.global.json",
},
}
There are probably better solutions out there, potentially including /// <reference path="..." /> tags, but this is what I have for now and it works. When people install my library, they have access to the types in intellisense, and if they want to use the types they can be imported.
I'm trying to compule the following typscript file
import { magic } from 'lib/magic';
magic();
The filestructure is:
./src/
main.ts
lib/
a/magic.ts
b/magic.ts
Inside tsconfig.json I map lib/magic to the right file as follows
{
"compilerOptions": {
"target": "esnext",
"module": "commonjs",
"outDir": "./dist",
"baseUrl": ".",
"paths": {
"lib/*": [ "src/lib/a/*" ]
}
},
"include": [ "./src/**/*.ts" ],
"exclude": [ "./node_modules" ]
}
It is that paths section that maps that import lib/magic to ./src/lib/a/magic.ts.
So, I can compile as follows
$> tsc -p ./tsconfig-a.json
It produces output in dist. However, when I try to run it
$> node ./dist/main.js
internal/modules/cjs/loader.js:626
throw err;
^
Error: Cannot find module 'lib/magic'
Require stack:
...
It makes sense, because in dist there is no such thing as lib/magic. Any suggestions how to fix this?
Until someone proves me wrong, here are my findings so far.
Here is an issue in which it is stated that all this is in fact expected behaviour.
So you basically have to fix it in a second step. In my case I want to make 2 build, one for a and one for b. The easiest way to fix it then is to have it working by default for a as follows
import { magic } from './lib/a/magic';
magic();
Then build b and replace that path as follows
sed -e 's/lib\/a/lib\/b/' ./dist/main.js > ./dist/main-node.js
In my case, one version was for NodeJs and the other for the browser, so I also needed to add the .js extension. Just in case anyone is interested, here is the command
$> echo "import { magic } from './lib/a/magic';" | sed "s#^\(import[^']*\)'\([^']*\)#\1 '\2.js#g"
Please let me know if you have a better solution, because all this feels a bit hacky
I have the following hierarchy:
dist/
|- BuildTasks/
|- CustomTask/
- CustomTask.js
node_modules/
source/
|- BuildTasks/
|- CustomTask/
- CustomTask.ts
- tsconfig.json
Additionally, I am trying to create a VSTS Task extension for internal (private) usage. Originally, I had my tsconfig.json at my root directory, and everything worked just fine on my local machine. The problem is that a VSTS Extension requires all the files to be included in the same directory as the task folder itself. See https://github.com/Microsoft/vsts-task-lib/issues/274 for more information:
you need to publish a self contained task folder. the agent doesnt run
npm install to restore your dependencies.
Originally, I had a this problem solved by include a step to copy the entire node_modules directory into each Task folder, in this case my CustomTask folder which contains my JS file. But, this seems a bit much considering that not every task I am writing has the same module requirements.
My idea was to create a tsconfig.json in each of the Task folders which would specify to create a single output file containing all of the dependent modules, but unfortunately it is not working:
{
"compilerOptions": {
"baseUrl": ".",
"target": "ES6",
"module": "system",
"strict": true,
"rootDir": ".",
"outFile": "../../../dist/BuildTasks/CustomTask/CustomTask.js",
"paths": {
"*" : ["../../../node_modules/*"]
}
}
}
Prior to adding the "paths", I was getting the following errors:
error TS2307: Cannot find module 'vsts-task-lib/task'.
error TS2307: Cannot find module 'moment'.
After adding the paths, I still get the error that it cannot find the module 'moment', which is in my node_modules directory. Also, when I look at the output JS it seems that it didn't include the 'vsts-tasks-lib' code necessary, maybe because it still had an error in regards to the 'moment' module? Not sure what I missed?
Using webpack to compile JavaScript modules, simple sample:
webpack.config.js:
const path = require('path');
module.exports = {
entry: './testtask.ts',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
node: {
fs: 'empty'
},
target: 'node'
};
After that, there are just bundle.js and task.json in task folder.
Update: sample code in testtask.ts:
import tl = require('vsts-task-lib/task');
import fs = require('fs');
console.log('Set variable================');
tl.setVariable('varCode1', 'code1');
tl.setTaskVariable('varTaskCode1', 'taskCode1');
var taskVariables = tl.getVariables();
console.log("variables are:");
for (var taskVariable of taskVariables) {
console.log(taskVariable.name);
console.log(taskVariable.value);
}
console.log('##vso[task.setvariable variable=LogCode1;]LogCode1');
console.log('end========================');
console.log('current path is:' + __dirname);
fs.appendFile('TextFile1.txt', 'data to append', function (err) {
if (err) throw err;
console.log('Saved!');
});
console.log('configure file path:' + process.env.myconfig);
console.log('configure file path2:' + process.env.myconfig2);
I have source code and tests separated as follows:
`src/main/ts/hello.ts` //SOURCE FILES HERE
`src/test/ts/hello.spec.ts` //SPEC FILES HERE
The import statement in src/test/ts/hello.spec.ts looks like this:
import hello from 'hello';
The hello.ts source code looks like this:
export function hello() {
return 'Hello World!';
}
export default hello;
My tsconfig.json is setup such that the test files can import source modules without using relative paths like this:
{
"include": [
"src/main/ts/**/*.ts"
],
"exclude": [
"node_modules"
],
"compilerOptions": {
"experimentalDecorators": true,
"noImplicitAny": true,
"moduleResolution": "node",
"target": "es6",
"baseUrl": ".",
"paths": {
"*": [
"*", "src/main/ts/*"
]
}
}
}
This way the hello.spec.ts file can import hello using the statement import hello from 'hello';
I'm trying to run the tests with npm test configured to run mocha and tsnode like this (Based on this article):
"scripts": {
"test": "mocha -r ts-node/register src/test/ts"
},
However it does not look like ts-node is picking up on my tsconfig.json configuration as I get this error:
mocha -r ts-node/register src/test/ts
Error: Cannot find module 'hello'
at Function.Module._resolveFilename (module.js:336:15)
at Function.Module._load (module.js:286:25)
The module resolution that you set through paths in tsconfig.json is purely an compile-time thing. (See this ts-node issue report and this TypeScript issue report for details.) It does not affect how the code is emitted, which means that your test file is doing a require("hello"), which Node cannot resolve. The consequence of paths being a compile-time thing is that your module loader needs to be configured to also perform the same kind of resolution that you specify in tsconfig.json. If you were using RequireJS, for instance, you'd need to have a configuration for it that does the same thing paths in tsconfig.json does. You are using Node, however...
What you can do in Node is use tsconfig-paths, which will read the tsconfig.json, parse the paths setting and change the module resolution in Node so that it works.
Using your code, I modified hello.spec.ts to have at least one test for feedback:
import hello from "hello";
import "mocha";
it("q", () => {
if (hello() !== "Hello World!") {
throw new Error("unequal");
}
});
I installed tsconfig-paths and #types/mocha (so that import "mocha" does the right thing compilation-wise in the test file I show above) and invoked Mocha like this:
$ ./node_modules/.bin/mocha --compilers ts:ts-node/register -r tsconfig-paths/register 'src/test/ts/**/*.ts'
I got this output:
✓ q
1 passing (20ms)
I want to split my application into different node modules and have a main module which builds all other modules as well and I want to use typescript with es6 modules.
Here is my planned project structure:
main
node_modules
dep-a
dep-b
framework
interfaces
IComponent.ts
dep-a
components
test.ts
node_modules
framework
index.ts
dep-b
node_modules
framework
I want to be able to define interfaces in framework which can be consumed in dep-a, dep-b and main.
How do I set up this correctly? Can I compile everything from my main-module? Do I need to create different bundles for framework, dep-a, ... and another typing file? What is the best approach for this?
I already set up some test files and folders and used npm link to link the dependencies and webpack to bundle the files and I am always running into issues with files not being found:
error TS2307: Cannot find module 'framework/interfaces/IComponent'
and
Module not found: Error: Cannot resolve 'file' or 'directory' ./components/test
TL;DR generate declarations for the modules using declaration: true in tsconfig.json and specify the file for your generated typings in the typings entry of the package.json file
framework
Use a tsconfig file similar to this:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"declaration": true,
"noImplicitAny": true,
"removeComments": true,
"outDir": "dist",
...
},
"files": [
...
]
}
The important bit is declaration: true which will generate internal declarations in the dist directory
Assuming there is an index.ts file which (re)exports all the interesting parts of framework, create a package.json file with a main and typings entry pointing to, respectively, the generated js and the generated declaration, i.e.
{
"name": "framework",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
...
}
Commit this module to a git repo, say bitbucket at : "https://myUser#bitbucket.org/myUser/framework.git"
dep-a
in package.json create a dependency to framework
{
"dependencies": {
"framework": "https://myUser#bitbucket.org/myUser/framework.git"
},
}
That is it.
import * from 'framework'
will pull the dependency with the typings, automatically
Obviously, it is possible to do with dep-a what was done with framework i.e. generate the declarations, update package.json and use dep-a as a module with embedded typings in main
note: a file URL will do in package.json/dependencies if you do not want go to via an external git repo
What arrived in TypeScript 1.6 is typings property in package.json module. You can check the relevant issue on GitHub.
So assuming you want to create separate modules ( dep-a, framework ). You can do the following :
main.ts // (1)
package.json // (2)
node_modules/
dep_a/
index.js // (3)
index.d.ts // (4)
package.json // (5)
node_modules/
framework/
index.js // (6)
index.d.ts // (7)
package.json // (8)
So let's see what you have in your files :
//(1) main.ts
import * as depA from "depA";
console.log(depA({ a : true, b : 2 }) === true) // true;
//(2) package.json
{
name: "main",
dependencies: {
"dep_a" : "0.0.1"
}
...
}
For depA
//(3) dep_a/index.js
module.exports = function a(options) { return true; };
//(4) dep_a/index.d.ts;
import * as framework from "framework";
export interface IDepA extends framework.IFramework {
a : boolean
}
export default function a(options: IDepA) : boolean;
//(5) dep_a/package.json
{
name: "dep_a",
dependencies: {
"framework" : "0.0.1"
},
...
typings : "index.d.ts" // < Magic happens here
}
For framework
//(6) dep_a/node_modules/framework/index.js
module.exports = true // we need index.js here, but we will only use definition file
//(7) dep_a/node_modules/framework/index.d.ts;
export interface IFramework {
b : number;
}
//(8) dep_a/node_modules/framework/package.json
{
name: "framework"
...
typings : "index.d.ts"
}
What I don't include in this answer ( for clarity ) is another compilation phase, so you could actually write the modules ( dep_a, framework ) with typescript and then compile them to index.js before you use them.
For a detailed explanation and some background also see: https://medium.com/#mweststrate/how-to-create-strongly-typed-npm-modules-1e1bda23a7f4