VS Code Intellisense for auto exported global variables - node.js

Sails exports a set og global variables, such as:
_ (lodash)
sails (framework related)
ModelOne, ModelTwo, ModelThree, ... (the sails models we define)
Creating a jsconfig.json targeting es5 doesn't help since the variables aren't explicitly declared.
I have this variables declared on eslintrc.globals, but that just tells the linter they exist.
Ideally I'd like to have intellisense for this variables across my project.
Is there any way I can declare this global variable types in VS Code?

At the time of this writing, looking at the npm page for sails.js, they don't provide type declaration files, and don't have community contributed type declaration files in the Definitely Typed project.
Your options are:
Contribute type declaration files to the Definitely Typed project for sails.js (see their contributing instructions), and then install your contributed types package (like npm install -D '#types/sailjs' (or whatever it gets called))
Create your own type declarations NPM package (outside of the Definitely Typed project) and use that. In that case, see the TypeScript guide on publishing declaration files.
Create a type declaration file (myfDeclarationFile.d.ts or something of your choice) and define those types there. Put the file anywhere where it will be picked up as part of your project based on your tsconfig.json/jsoconfig.json file.
Note that in this case, you might need to make sure the name of the .d.ts file you create might need to not have the same name as other .ts files in the same directory (see https://github.com/microsoft/TypeScript/issues/51128).
In your particular case, since these are type declarations for globals, see TypeScript's guide for declaring types for global libraries. For guides for other types of libraries, see the guide index page.

Related

How can I make vscode assume a node.js context, so that it doesn't assume `fetch` exists?

By default, when editing a JavaScript file in VSCode, it will assume that the fetch function and related types exist in the current context. This makes sense for JavaScript designed to run in the browser, but when running on node.js the fetch function does not exist unless it is installed via node-fetch. I find that in this context, VSCode is misleading, as it will not highlight an error when you trying calling the fetch function, or access other types such as Request and Response, even though they do not exist unless you have node-fetch installed.
How can I configure vscode to assume a node.js context, and therefore assume that fetch does not exist unless I explicitly import it from node-fetch?
Why web types are there by default
From the docs for tsconfig.json compilerOptions.lib:
TypeScript includes a default set of type definitions for built-in JS APIs (like Math), as well as type definitions for things found in browser environments (like document).
How to change the defaults
Create a tsconfig.json or jsconfig.json, and set the compilerOptions.lib array to not contain "DOM", which means that lib.dom.d.ts (the "DOM standard library" type definitions file that comes with TypeScript) will not be assumed. You should specify which ECMA Script standard you want to write your source code in.
The config file also has fields to control what files it takes effect on: files, include, and exclude. If you specify none of them, include will default to **, which matches everything beside and recursively under subdirectories beside the config file.
Having to create this file could be seen as annoying if you just want to write a single JS file (ie. now you have a whole config file just for one source file!). I don't know if there are alternatives that are more convenient for such a use case. If anyone knows, please edit this answer.
I looked briefly into TypeScript triple-slash directives, which allow specifying things on a per-file basis, but I think you can only add things (ie. I don't think you can use them to remove a lib).
At the time of this writing, there are VS Code settings that can be applied at the user-settings scope that affect settings for implicit projects (JS/TS files which don't have a project config file) (js/ts.implicitProjectConfig.*), but none of them are for setting the compileOptions.lib field, and my gut says it's probably not going to happen (but don't quote me on that).
You probably also want types for the Node API
Use npm to install a version of #types/node. Make sure the major version number of the version you install matches the major version number of the version of Node JS you want to script to be runnable on.
Fun irrelevant facts to this question
Continuing on the point about VS Code's user-settings for implicit projects, VS Code puts some defaults in effect (on top of those that TypeScript itself does) if no project is detected. You can poke through the code at github.dev/microsoft/vscode by doing "Find in Files", using extensions/typescript-language-features/**/* as the "files to include" field, and compilerOptions as the find query. compilerOptions.lib seems to not be something that VS Code touches in such a scenario.

How to export types in a TypeScript npm module

In TypeScript, say I want to have the user use my module's "internal" types so they can properly type their own variables when using my module - do I just export literally everything from my index.ts file to accomplish this?
// index.ts
export * from './file1' // uses types/interfaces defined in file1types
export * from './file2' // uses types/interfaces defined in file2types
export * from './types/file1types'
export * from './types/file2types'
Do .d.ts files help me accomplish this, or are they only for non-TS projects? Does tsconfig.json's option declaration: true help me accomplish this by generating a .d.ts for every TS file? Is this an alternative to exporting everything from a single index.ts file?
And if declaration: true does help me accomplish this, how would the user use all those generated .d.ts files within the build folder?
I would greatly appreciate some clarification as to how one typically exports types in TS projects. Thanks in advance.
For those who are like me, and have made sure declaration: true is set in your tsconfig.json, and that your build process correctly creates the corresponding .d.ts files to the appropriate directory pointed to by your package.json file, AND STILL somehow can't access the internal types of your module when testing on an external project -- try restarting the TS server in VSCode (assuming you're using VSCode)
So much time was wasted trying to figure this out, only to realize Typescript was functioning fine and I was being sabotaged by my IDE.
Without declaration files you can develop a package in TypeScript, compile it and expose it to other users as JavaScript code. Including them also allows TypeScript developers to use the package with any types you defined in it. They can get more type information whilst working with your library, such as required arguments types, function return types etc, as well as warnings from their IDE/Intellisense when there are conflicts.
The declaration: true in the file tsconfig.json instructs the TypeScript compiler to output declaration files (.d.ts). Often they are bundled in a single file e.g. index.d.ts and then a "types": path/to/index.d.ts field is added to the library's package.json file to inform TypeScript where to look for the types (when a user imports the package).

TypeScript ES6 namespaces

I have a Node.js project I recently converted to TypeScript from ECMAScript 6-compatible JavaScript. I am still not entirely familiar with how TypeScript and ES6 interact, especially in regards to namespaces and types.
I have a subfolder in my project called sql which, as it sounds, exports a number of functions to interact with the project's MySQL database. I would like to organize my DB model types in a Sql.Models namespace, so they can be referenced e.g. Sql.Models.User. I would like my models to be declared in one or more .d.ts files inside the sql folder, but I can't figure out how to properly organize this so that these types can be referenced elsewhere. I obviously cannot require or import a .d.ts file; that's not a valid TypeScript operation. How can I use the type Sql.Models.User declared in a .d.ts file in another ES6-compatible TypeScript module?
How can I use the type Sql.Models.User declared in a .d.ts file in another ES6-compatible TypeScript module?
The following very simple setup works.
root
sql
models.d.ts
index.ts
tsconfig.json
The model.d.ts file declares a namespace and a type. As the image demonstrates, the index.ts file can alias that namespace and/or access it directly.
The above is using TypeScript version 2.2.1.
I solved this in a less-than-pretty fashion by using "aliased internal imports" which sound strange but really aren't. I export an alias to an internal symbol, which just happens to be a symbol imported from another file.
I would have preferred something like how C# works, where I can define the namespace in two files and somehow alias one from another so that only one file needs to be referenced, but this doesn't seem possible with ECMAScript 6-compatible TypeScript.

How can I include additional modules in a NodeJS custom binary?

I am building a custom binary of NodeJS from the latest code base for an embedded system. I have a couple modules that I would like to ship as standard with the binary - or even run a custom script the is compiled into the binary and can be invoked through a command line option.
So two questions:
1) I vaguely remember that node allowed to include custom modules during build time but I went through the latest 5.9.0 configure script and I can't see anything related - or maybe I am missing it.
2) Did someone already do something similar? If yes, what were the best practices you came up with?
I am not looking for something like Electron or other binary bundlers but actually building into the node binary.
Thanks,
Andy
So I guess I figure it out much faster that I thought.
For anyone else, you can add any NPM module to it and just add the actual source files to the node.gyp configuration file.
Compile it and run the custom binary. It's all in there now.
> var cmu = require("cmu");
undefined
> cmu
{ version: [Function] }
> cmu.version()
'It worked!'
> `
After studying this for quite a while, I have to say that the flyandi's answer is not quite true. You cannot add any NPM module just by adding it to the node.gyp.
You can only add pure JavaScript modules this way. To be able to embed a C++ module (I deliberately don't use the word "native", because that one is quite ambiguous in nodeJS terminology - just look at the sources).
To summarize this:
To embed a JS module to your custom nodejs, just add it in the library_files section of the node.gyp file. Also note that it should be placed within the lib folder, otherwise you'll have troubles requiring the module. That's because the name/path listed in node.gyp / library_files is used to encode the id of the module in the node_javascript.cc intermediate file which is then used when searching for the built-in modules.
To embed a native module is much more difficult. The best way I have found so far is to build the module as a static library instead of dynamic, which for cmake(-js) based module you can achieve by changing the SHARED parameter to STATIC like this:
add_library(${PROJECT_NAME} STATIC ${SRC})
instead of:
add_library(${PROJECT_NAME} SHARED ${SRC})
And also changing the suffix:
set_target_properties(
${PROJECT_NAME}
PROPERTIES
PREFIX ""
SUFFIX ".lib") /* instead of .node */
Then you can link it from node.gyp by adding this section:
'link_settings': {
'libraries' : [
"path/to/my/library.lib",
#...add other static dependencies
],
},
(how to do this with node-gyp based project should be quite ease to google)
This allows you to build the module, but you won't be able to require it, because require() function in node can only be used to load built-in JS modules, external JS modules or external dynamic node modules. But now we have a built-in C++ module. Well, lot of node integrated modules are C++, but they always have a JS wrapper in /lib, and those wrappers they use process.binding() to load the C++ module. That is, process.binding() is sort of a require() function for integrated C++ modules.
That said, we also need to call require.binding() instead of require to load our integrated module. To be able to do that, we have to make our module "built-in" first.
We can do that by replacing
NODE_MODULE(mymodule, InitAll)
int the module definition with
NODE_BUILTIN_MODULE_CONTEXT_AWARE(mymodule, InitAll)
which will register it as internal module and from now on we can process.binding() it.
Note that NODE_BUILTIN_MODULE_CONTEXT_AWARE is not defined in node.h as NODE_MODULE but in node_internals.h so you either have to include that one, or copy the macro definition over to your cpp file (the first one is of course better because the nodejs API tends to change quite often...).
The last thing we need to do is to list our newly integrated module among the others so that the node knows to initialize them (that is include them within the list of modules used when searching for the modules loaded with process.binding()). In node_internals.h there is this macro:
#define NODE_BUILTIN_STANDARD_MODULES(V) \
V(async_wrap) \
V(buffer) \
V(cares_wrap) \
...
So just add the your module to the list the same way as the others V(mymodule).
I might have forgotten some step, so ask in the comments if you think I have missed something.
If you wonder why would anyone even want to do this... You can come up with several reasons, but here's one most important to me: Those package managers used to pack your project within one executable (like pkg or nexe) work only with node-gyp based modules. If you, like me, need to use cmake based module, the final executable won't work...

cmake, lost in the concept of global variables (and PARENT_SCOPE or add_subdirectory alternatives)

I have a cmake project in which I have some modules and I'm using Find-*.cmake for including the shared modules in the application.
For not taking in account every module that I add, I have defined a kind of global LIB variables tor the linker:
# inside a Find-*.cmake or in the CMakeLists.txt of the modules:
set(LIB ${LIB} ...)
so in one of the final applications that uses some modules I can just do:
target_link_libraries(${APP_NAME} ${LIB})
Then, I'd like to have the compiled modules in the /project_path/modules/foo/build so that if a module is really big to compile it can be compiled once for all the application that use it. The way I'm achieving this is to load the CMakeLists.txt of the module from the Find-*.cmake in this way:
# Inside FindFoo.cmake, for loading /project_path/modules/foo/CMakeLists.txt
# and compile it in /project_path/modules/foo/build
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../modules/${PACKAGE_NAME}
${CMAKE_CURRENT_LIST_DIR}/../modules/${PACKAGE_NAME}/build
)
include_directories(${CMAKE_CURRENT_LIST_DIR}/../modules/${PACKAGE_NAME}/include)
But it happened sometimes that some module require another modules so that the add_subdirectory creates new scopes and can correctly load LIB but cannot write it (when I use set it is in the deeper scope and not changes the upper scope). For bypass this I have to add PARENT_SCOPE in the set).. So I have tried to add it in some module that I think could be nested and hidden in some dependencies but compiling all the application I suddenly faced with:
CMake Warning (dev) at /path_to_repo/cmake/FindFooX.cmake:6 (set):
Cannot set "LIB": current scope has no parent.
Call Stack (most recent call first):
CMakeLists.txt:14 (find_package)
This warning is for project developers. Use -Wno-dev to suppress it.
I am afraid that this can change from app to app in respect to which module I need or in respect of the dependecies tree in the modules itself so I'm looking for a cleaner solution.
You can 'simulate' GLOBAL variable behavior, by using properties with GLOBAL scope :
SET_PROPERTY(GLOBAL PROPERTY MyGlobalProperty "MyGlobalPropertyValue")
Then you can extract your global property by using
GET_PROPERTY(MyLocalVariable GLOBAL PROPERTY MyGlobalProperty)
Then, MyLocalVariable contains "MyGlobalPropertyValue".
Because PARENT_SCOPE extends variable definitions to the only parent directory (and not to its parents), there are cases it's not enough, for example if you have a deep source tree...
All variables in CMake are local by default. While you can use the PARENT_SCOPE parameter to increase the scope of a local variable by one layer, that mostly makes sense for return values of functions.
For find scripts on the other hand you usually want the behavior of a global variable: Once the find script is called by anyone, you want the results to be available everywhere. In particular, a second call to the same find script should just reuse the results of the first call.
In CMake this is achieved by storing variables to the cache. The various find_* calls already do this automatically, so you should prefer using those where applicable. For any additional custom variables, set offers the capability to store to the cache as well:
set(MY_GLOBAL_VARIABLE "Some value" CACHE STRING "Description")
Note that local variables can hide cached variables of the same name in their scope.

Resources