I am creating a project with node on the server and Angular 2 on the client. I want to use Typescript across the entire project. I would like to target ES6 for the server, since node supports it, but ES5 for the client. Currently my directory structure looks something like this.
├── app.js
├── something.js
├── tsconfig.json
├── wwwroot
│ ├── index.html
│ ├── main.ts
│ ├── components
│ │ ├── mycomponent.ts
I want everything above wwwroot to target ES6, but everything inside of wwwroot to target ES5. I tried putting a second tsconfig.json inside the wwwroot folder, but that didn't seem to work. I am using Atom's autocompile feature, if that is of any relevance.
You can use babel to transpile es6 javascript to whatever flavor of ecmascript you desire.
http://babeljs.io/
I have a similar situation, and what I'm doing is:
- project-root
- scripts
- tsconfig.json
- wwwroot
- scripts
- tsconfig.json
- html
- index.html
- styles
- index.css
The scripts/tsconfig.json targets es6 for the server, and the wwwroot/scripts/tsconfig.json targets es5 for the client.
You can use Babel to transpile to es5 what nodejs does not already implement, using the preset "babel-preset-es2015-node6".
You can transpile only the directories that you want to es5 using for example a script in your node package.json:
"prestart": "babel server-source --out-dir dist/source"
Related
I a pnpm and turborepo monorepo with two apps, a Fastify API and a React frontend. Both apps are bootstrapped using modular components (frontend components, Fastify Plugins, etc.) that are also their own separate packages in the monorepo. I'm trying to refactor and put the things that are shared between the main apps and the components into one single helpers package.
.
├── apps/
│ ├── ui
│ └── api
├── modules/
│ ├── api-module-1
│ ├── api-module-2
│ ├── ui-module-1
│ └── ui-module-2
└── packages/
└── helpers/
├── isomorphic-function-01
├── ui-function-01
├── ui-function-02
├── api-function-01
└── api-function-02
The apps have the modules and packages as workspace dependencies. A UI function can be used with any UI module or the main UI app, and any API function can be used with any UI Module or the main API app and there can also be pure functions that can be used both on the browser and in Node.
The problem is if I export something in the helpers package that is browser specific like a React Hook then the API crashes because it has something that won't load in Node. And if I export something that is API specific such as ORM related, then the frontend crashes as it is not able to load it in the browser.
I am using tsup for prod builds and tsx for dev in the API app, and Vite for the frontend.
The exports in the helpers package is done using a barrel file. I'm also using TypeScript and ESM. If I comment out the API function, UI app will run fine and vice versa.
export * from './isomorphic-function-01';
export * from './ui-function-01';
export * from './api-function-01';
I had expected the apps/modules to only use what is imported and not load the entireity of the helpers package's exports. I can split this into 3 helpers packages for UI, API and Isomorphic and get it to work but is there a way I can keep one package and only allow them to import what is required?
I have a Create-React-App 5 project using Yarn 3 and TypeScript 4. The web app uses a library in the same repo through Yarn's portal: protocol (Yarn workspaces are not used).
myRepo
├── myLib
│ ├── package.json
│ ├── yarn.lock
│ └── src
│ ├── index.js
│ └── macro.js
├── myApp
│ ├── package.json
│ ├── yarn.lock
│ └── src
│ ├── App.tsx
│ ├── App.test.tsx
│ └── index.tsx
└── yarnrc.yml
myApp/package.json uses a portal like so:
"dependencies": {
"myLib": "portal:../myLib",
My top-level Yarn config:
# yarnrc.yml
nodeLinker: node-modules
If I substitute file: for portal:, everything works fine. Even with portal:, yarn build and yarn start within myApp still work fine, but yarn test becomes a problem: /Users/luke/myRepo/myLib/src/index.js: The macro imported from "./macro" must be wrapped in "createMacro" which you can get from "babel-plugin-macros".
Huh? I'm certainly not trying to do anything with Babel macros. Moreover, that stuff is in a library and not the app itself.
What's going on, and how do I fix it?
Okay, I think I've figured it out!
First, CRA includes babel-plugin-macros (source), which operates by looking for particular filenames such as macro.js and treating them as Babel macros (docs). So, if I had a file myApp/src/macro.js, it would error out in a similar way, but also for build/start.
Second, why is it only a problem under Jest? Jest is actually transpiling the dependency, which Webpack somehow knows not to do. Normally this would be inconsequential (apart from slowing things down), but in this case Jest's transpiling is what runs into the problem, since it wrongly tries to interpret myLib using Babel macros (which myLib's author of course never intended).
Third, why is Jest trying to transpile that other library, which lies outside myApp, in the first place? It certainly shouldn't be. Actually, Jest tries to transpile everything besides what is explicitly excluded, and the exclusion under CRA defaults to (source):
transformIgnorePatterns: [
'[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$',
'^.+\\.module\\.(css|sass|scss)$',
],
The intent is to include the app itself and exclude its dependencies. This approach sort of assumes that all the dependencies of myApp will be somewhere under a node_modules, which (PnP aside) would be an especially reasonable expectation under nodeLinker: node-modules. But with a portal, there is a symbolic link to myLib, which is (after resolving its real path) not under any node_modules and therefore does not match the ignore patterns. Thus, dependencies accessed through portals are not excluded from Jest transformation under this config.
Solution
To solve it, configure Jest to also exclude anything not under the project's root directory, i.e., add:
'^(?!<rootDir>)'
Since I happen to be using Craco already, I can just:
// craco.config.js
module.exports = {
jest: {
configure: jestConfig => ({
...jestConfig,
transformIgnorePatterns: [
...jestConfig.transformIgnorePatterns,
'^(?!<rootDir>)',
],
}),
},
};
There really ought to be a built-in way to handle this, but 🤷♂️
I have just published a new TypeScript-based module to the NPM registry, ooafs. However, when I try to install it and import it in another TypeScript project, VSCode gives me the following error on the import statement: Cannot find module 'ooafs'.ts(2307).
This module's source files are compiled to JavaScript to a dist/ folder and definitions (.d.ts) are also generated.
Here's the tree of the published module (the one we download when we npm install):
.
├── dist
│ ├── Entry.d.ts
│ ├── EntryFilter.d.ts
│ ├── EntryFilter.js
│ ├── Entry.js
│ ├── EntryType.d.ts
│ ├── EntryType.js
│ ├── FSTypings.d.ts
│ ├── FSTypings.js
│ ├── index.d.ts
│ └── index.js
├── LICENSE
├── package.json
└── README.md
The package.json does contain the following entries:
{
"main": "dist/index.js",
"types": "dist/index.d.ts",
...
}
Because the module works normally on Runkit (pure JS), I assume the only problem I have is related to TypeScript, and it's not the first time TypeScript tells me a module doesn't exist when missing declaration files are the only problem.
Am I missing a step in the compilation process ?
Are my package.json properties wrong ?
If you need to see more code, the Github link is at the beginning of the question, and the published module structure can be found here: https://unpkg.com/ooafs#0.1.2/dist/.
Actually, the problem didn't come from my module (ooafs). It was a problem with the tsconfig.json of the project I was using the module in: The module property must be set to commonjs apparently.
Very late edit:
Also, I highly recommend setting esModuleInterop to true which allows you to import non-es6 modules in a more natural manner.
The answer is not the fix, and is certainly not ideal when you have to use top-level awaits (which don't work on commonjs).
You want to make sure your import path is the final file that node will try and load. So you cannot rely on folders resolving to folder/index.js and you cannot rely on giving file names without extensions (give the ".js" extension)
I want to create a library in Typescript that I can share via npm. Specifically, I want to use webpack to generate a js bundle along with a definition file to share the types with the js. So I'd have a tree of files like:
├── lib
│ ├── lib.d.ts
│ └── lib.min.js
├── test
...
├── ts
│ ├── errors
│ │ ├── CannotModifyAlteredObject.ts
│ ├── Lib.ts
│ ├── PostProcessors.ts
│ ├── Serializers.ts
├── tsconfig.json
├── typings.json
├── LICENSE
├── package.json
├── README.md
└── webpack.lib.config.js
And all the types exported by ts/Lib.ts would be exported to a single .d.ts in the lib directory to sit next to the js bundle.
I've looked at the following questions/sources:
Writing npm modules in typescript
How to create a typescript library (and the question it duplicates)
This unanswered question
The offical typescript guide to creating packages
This example typescript library project
And another SO question
However, none of these provide an example using webpack. Being able to bundle everything you need to use the library (apart from the nodejs runtime) into a single file is pretty important for my use case, so webpack fits this role well. I'd like to be able to generate a .d.ts file that maps to what webpack creates. However, I want to avoid creating the .d.ts file manually - it should be possible to automatically extract the types without having manually created .d.ts files get out of sync with my source code. Is there a way of doing this?
So I'm working on this project that has a Rails-like folder structure though it's handled by Node.js tooling (Grunt as task runner). I'm using Bower to manage my vendor assets.
My folder structure looks like this:
.
└── src
├── app
│ └── assets
│ ├── javascripts
│ └── stylesheets
│ └── application.scss
├── public
└── vendor
└── bower
Basically all the development source code lives in the app/assets folder, public is where production files go and vendor is where 3rd party stuff goes.
So as you can see, I have this application.scss file. This is the stylesheet manifest I'm using. It's responsible to import all the modules that should be compiled to my final stylesheet file later.
The problem is that I don't see a sane way to reference libraries installed through Bower from inside my manifest file.
With Rails Asset Pipeline/Sprockets I would do //= require_tree vendor/bower and that would work but I don't know what's the equivalent of doing that on the context of this project.
Do you guys have any suggestion on what could I do?
Ps.: Using Grunt tasks to "handle" this is out of question.
Just configure Bower to install packages on vendor/assets/components by creating a file called .bowerrc in your root directory.
{"directory": "vendor/assets/components"}
Everything inside vendor/assets and app/assets is added to the load path, so you can just reference those files.
You may need to put the actual file you want to load. Let's say you installed the normalize-scss package. You'll probably have to add this to your application.scss file:
#import "normalize-scss/normalize";
This is just a guess, but I'd bet on it.
EDIT: This will work on Rails apps, which apparently isn't your case. So if you're using Grunt to compile SCSS, you can add the Bower directory to your load path with the loadPath option.
The Gruntfile's Sass task may look with something like this:
{
sass: {
dist: {
files: {"src/assets/stylesheets/application.scss": "public/application.css"},
options: {
loadPath: ["vendor/bower"]
}
}
}
}
To import the file, you will do something like I said above (referencing the whole file). Didn't test the Grunt configuration, but it'll probably work.
Bower downloads whole git repositories.
Example:
bower install jquery
This would create the following structure
tree vendor/bower
bower
└── jquery
├── bower.json
├── component.json
├── composer.json
├── jquery.js
├── jquery-migrate.js
├── jquery-migrate.min.js
├── jquery.min.js
├── jquery.min.map
├── package.json
└── README.md
1 directory, 10 files
It doesn't make much sense to load all those files in my opinion.
What You could do is:
create a vendor/require directory
symlink all required files into this directory:
cd vendor/require; ln -s ../bower/jquery/jquery.min.js
then require all files with ruby's help or manually
Dir['path/to/vendor/require/*.js'].each do |file_name|
puts "<script type="text/javascript" src="#{file_name}"></script>"
end
You could also use Grunt and it's concat task:
grunt.initConfig({
concat: {
options: {
separator: ';',
},
dist: {
src: ['path/to/vendor/bower/jquery/jquery.min.js', 'path/to/vendor/bower/other-package/init.min.js'],
// or if You decide to create those symlinks
// src: ['path/to/vendor/require/*'],
dest: 'path/to/public/js/built.js',
},
},
});
For compass on sass You could use:
#import 'path/to/directory/*';