I have a javascript file in the public folder and I want to import that file to components in the folder src/components.
projectFolder
publicFolder
index.html
recorder.js
srcFolder
componentsFolder
Speech.js
Speech.css
But I can't do something like this in my component:
import Recorder from '../../public/recorder'
Because I get the following error:
Module not found: You attempted to import ../../public/recorder which
falls outside of the project src/ directory. Relative imports outside
of src/ are not supported. You can either move it inside src/, or add
a symlink to it from project's node_modules/.
As I've understood it's not allowed to import outside of /src directory, so I was wondering how I could "add a symlink" or if you know other ways to fix it.
I believe you are using create-react-app ... this is a feature included in the ModuleScopePlugin, and you can disable it by ejecting the app and editing your webpack configuration (as described in this answer).
But beware, the feature exists for a reason. The create-react-app build tool only processes the src/ directory, so for example your JavaScript outside of here will not be transpiled by Babel. Furthermore, you're typically trying to avoid polluting the global scope if you're using a bundler like Webpack. So unless you've got a really specific reason why you'd need to do this, I'd say try and move it.
You can modify the react-scripts config with the rescripts library
Create a file called .rescriptsrc.js in your root folder:
module.exports = config => {
const scopePluginIndex = config.resolve.plugins.findIndex(
({ constructor }) => constructor && constructor.name === "ModuleScopePlugin"
);
config.resolve.plugins.splice(scopePluginIndex, 1);
return config;
};
If you're ok not using the actual file in public and having a duplicate js file in the src directory there's a hacky but cool solution if you use vscode. This vscode extension https://marketplace.visualstudio.com/items?itemName=emeraldwalk.RunOnSave allows you to trigger a command on save (you can use regex to specify which file saves should trigger which commands) and you can specify a command to run on save as such:
{
"match": "public\\\\recorder.js$",
"cmd": "copy ${file} ${workspaceFolder}\\src\\recorder.js"
}
Now you can import from that duplicated file.
As i can see you want to import parent component in child component.
While defining path './' represents the current directory in which you are working so you can go one level up by following '../' for one level up and the same goes for the upper level directory.
So if you want to import from public folder inside Speech.js component you could do something like this.
// In Speech.js
import Recorder from './../../public/recorder';
Hope this will be useful to you.
Related
I'm working on a package that I am planning to publish publicly on npmjs. Let's call it the "text package".
I would like that by default when installing that package, you can import .txt files directly and get the correct type (out of the box), like this:
import text from './file.txt'
The text variable would be of type string because the package would have defined its type, using something like this (in a global.d.ts):
declare module '*.txt' {
export const text: string;
export default text;
}
If I include that global.d.ts in my package, and that I import something from this package, then I will automatically get the correct type when importing a .txt file.
But the problem is sometimes I would just need to import a .txt file without importing anything from the "text package", which is why I was wondering if there is some sort of way, as you install a package to install a global type that does not require to import anything else for the type to apply.
In other words, as soon as you install my "txt package" the declare module '*.txt' would apply to my entire project out of the box.
Is there even a way to do this, or whoever installs my package would have to declare their own global type (e.g., declarations.d.ts) to be able to import .txt files globally?
I know that even if the import type works, it will still require Webpack or another bundler to really work but this question is just about the type.
The short answer is:
TypeScript does not support Global types without importing the file referring to the type.
More details:
One example that I found doing this was Next.js - when creating a TypeScript app using npx create-next-app#latest --typescript you can start importing *.css files (for example) and get the correct type.
Where I got confused is that I originally thought that the type was coming from the next-env.d.ts but even when I deleted the file, *.css import was still working in Visual Studio code. But the reason it was, is because a file in the pages directory were importing Next.js' index.d.ts file.
Basically, in Visual Studio Code, as soon as your import a type somewhere in your project, if it's global, it will be accessible everywhere.
Workaround
So what can be done with the current TypeScript capabilities? To support new file types, you will need a file loader such as Webpack. The logical thing to do would be to add a reference to the file type declaration in the file loader itself. This way, as soon as you configure your file loader to be able to import the file, you will inherit the type:
create a txt.d.ts in our package's source directory (e.g. src) - you can use any name for the file, it's not important
if you are using eslint, add an entry to ignore the type file (e.g. 'src/*.d.ts' in your ignorePatterns option
Since you are adding a d.ts file in your source that is not managed by tsc, you need to add a script that will perform the following actions:
Copy txt.d.ts in the target directory of the compiled files for your package
Add this line at the top of your package's file loader (e.g. loader/index.d.ts: /// <reference types="../txt" />\r\n - this will link the declaration file back into your package. Note that you can add this reference to any file of your package.
This workaround will only work once you import the file referencing back to the declaration - this is the only way TypeScript can be made aware that this type exists (see https://github.com/microsoft/TypeScript/issues/49124).
Another alternative could also be to add manual steps (in a readme file) to add a global type declaration file.
to bundle global types, do the following. form typescript
specify default typings directory at typeRoots. .e.g "typeRoots": ["./src/types"].
create a file /src/types/global.d.ts in the specified directory
declare your types in the file inside declare global {} and make sure to have export {} if you don't already export anything.
I have a plain old NodeJS project (with Typescript) and I'm really struggling to find out how to do ES6 imports for local files without having to do "../../../foo/bar" all the time.
There are loads of similar questions but all seem to revolve around babel/Webpack which I'm not using.
If I do the following:
import foo from `/bar`
it looks for it in the root folder of my PC (e.g. c:/bar) and fails.
I have tried using a .env file with NODE_PATH set to various hings ("/", "." etc) but no luck. I have also tried setting "type: 'module'" in my package.json and my tsconfig.json file has {"baseUrl": "."}
So I think I've tried every answer I can find. Am I just doing them in the wrong combination or is the solution something different?
Here are two tricks I've used for this, with Node.js and native ES modules.
file: dependencies
If you want to access <project root>/bar from a sub package two levels down, adding this to the package.json dependencies:
"#local/bar": "file:../../bar",
..makes bar available to the said subpackage as #local/bar.
While relative paths are still present, they are now all in the package.json files and the sources never need to know..
Use dynamic import()
Pick the root folder's path to a constant and do this:
const foo = await import(`${rootPath}/bar`);
This was pretty simple to implement for me, using Typescript in VSCode.
In tsconfig.json, I added "baseUrl": "./", under compilerOptions
After a restart of VSCode, VSCode will automatically import using psuedo-absolute paths.
The paths won't begin with /, that still points to your drive root.
If the is below the current file, it will still use a ./relative/path, but no more ../../tree/traversing
Then I set dev in packages.json to
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "cross-env NODE_PATH=./ nodemon ./app.ts"
},
I use cross-env. You may use the set command and nodemon for automatic reloading of change files.
Setting NODE_PATH to ./ tells NodeJS to do what Visual Studio and TypeScript are doing.
I do have package.json in my src directory. You may not, and may need to change some pathing to adjust.
I am not sure what you mean by "local files without having to do "../../../foo/bar""
Let me explain how Javascript handles imports. Here is a basic folder structure.
C:Users/NAME/Project/randomfolder/bar.js
- Project
- Random Folder
- foo.js
- bar.js
Option 1 Probably the better option for 95% of what you will do
Let's say you are trying to import foo.js into bar.js we first get our current location of bar.js using a . so it would be
import foo from "./Random Folder/foo.js"
If you are going the other way the . is still used to say get current location in the folder structure but you pass a second . so .. then a / to go up a folder. So to import bar.js into foo.js it would look like this:
import bar from "../bar.js"
We are going up a folder then looking for the file we want.
Option 2
But if you know you folder structure is going to be very big and you will be importing always a few folders up or down why not make a variable and then use string literals.
let folder = `../../../`
import test from `${folder}foo`
You have a few options of how to handle what you want to do.
Option 3 For NodeJS and modules
If you are using NodeJS and want to get the path not relative, the use the app-root-path module. It lets you always get your project root and then dig into to files accordingly. This can be accomplished with string literals.
var appRoot = require('app-root-path');
import foo from appRoot + "/foo/bar/folders/bla/bla"
or
import foo from `${appRoot}/foo/bar/folders/bla/bla` <-- string literals
I am creating a VSCode extension, and following the getting started guide (https://code.visualstudio.com/api/get-started/your-first-extension) have used yeoman scaffold to get started. I created a new file, newModule.js in the same directory and want to import it as a module for use in the main extension.js script. I then do:
const newModule = require('./newModule.js');
This throws an error:
cannot find module 'newModule' require stack: -
This problem disappears if I copy my file to the node_modules folder created by default. I would like to know what is going on here, and what the best way of handling imports is when working with javascript/Node.js/vs-extensions.
I also notice that node_modules folder is not pushed to github by default, why?
The node_modules folder is for storing all the code from the libraries and packages you are using. It is excluded from git because it is a waste of space and a distraction to store them all in your versioning-control, as you can just re-download them anytime.
Just put your module in the same /src directory, and use the import syntax to import it, instead of require.
import newModule from './newModule';
For example, see how it is done in this sample code.
Please instead
const newModule = require('./newModule.js');
Try this
import newModule from './newModule');
// ^^ Do not use file extension
Also make sure that the file you are calling is in the same directory
I just want to know one thing,
Consider i have defined a dependency in package.json file like "some-library": "1.0.0"
and installed it using npm install. which will include all dependencies to node_modules folder.
then am importing a Component from that dependency using
import SomeLibrary from 'some-library;
when we do this where this import statement start looking for the component which we are importing ?
can some one explain in a better way. i have googled alot but didn't find any relevant answer. Thanks in advance.
At it's core, the import statement uses the same module resolution method as require().
So for installed modules it goes like this: By calling require(X), it gets a list of all the 'node_modules' directories present in the parent directories. Then, it tries to load the X module from each of those directories (either as a single file, or a directory.)
https://nodejs.org/api/modules.html#modules_all_together
I've been working on creating a small library of React components for use in several other projects. I am publishing the package internally (using a private GitHub repository) and then including in another project. However, when I go to import from a subdirectory of the package I am not able to do so as the paths don't match.
The projects using the package all utilize webpack to bundle/transpile code as I am trying to avoid doing any building in the component library if possible.
Directory Structure
- package.json
- src/
- index.js
- Button/
- index.js
- Button.jsx
- ButtonGroup.jsx
- Header/
- index.js
- Header.jsx (default export)
package.json
...
"main": "./src/index.js",
"scripts": "",
...
src/Button/index.js
import Button from './Button';
import ButtonGroup from './ButtonGroup';
export default Button;
export { Button, ButtonGroup};
src/index.js
Is this file actually necessary if only importing from subdirectories?
import Button from './Button';
import ButtonGroup from './Button/ButtonGroup';
import Header from './Header';
export { Button, ButtonGroup, Header };
Other Project
// This project is responsible for building/transpiling after importing
import { Button, ButtonGroup } from 'components-library/Button';
Example
Material-UI is a library of React components that is used by requiring in the following fashion: import { RadioButtonGroup } from 'material-ui/RadioButton. I've tried to figure out how this works for them but to no avail yet.
Similar Questions
How would I import a module within an npm package subfolder with webpack?
This is very nearly the correct approach I require, except that the import path used there involved the src/ directory, which I am trying to avoid (should be component-library/item, not component-library/src/item (which does work currently though))
Publishing Flat NPM Packages
This is exactly what I want except that I was hoping to not have a "build" phase in the package (rely on importing locations to build/transpile)
Questions
Can I skip the src/ directory somehow in the import path?
Can I skip any type of build phase in the package (so developers don't have to build before committing)?
How does a package similar to material-ui handle this?
Can I skip the src/ directory somehow in the import path?
Yes. Using the package.json "exports" field, which should be supported by Webpack in a near future (see this issue), but has already been supported by Node since Node 12 LTS following the Bare Module Specifier Resolution proposal:
package.json
...
"main": "./src/index.js",
"type": "module",
...
"exports": {
"./Button": "./src/Button/index.js",
"./Header": "./src/Header/index.js"
},
...
Now, the following code:
// This project is responsible for building/transpiling after importing
import { Button, ButtonGroup } from 'components-library/Button';
should be translated to:
import { Button, ButtonGroup } from 'components-library/src/Button/index.js';
which should correctly import the requested modules.
Caveat
Now, it would certainly be tempting to try a simpler version like:
...
"exports": {
"./Button": "./src/Button/",
"./Header": "./src/Header/"
},
...
so as the usual import statement
import { ... } from 'components-library/Button';
gets translated to
import { ... } from 'components-library/src/Button';
This looks nice, but it will not work in this case, because your submodules don't have each their own package.json file but rely on their index.js file to be found.
/!\ Unlike in CommonJS, there is no automatic searching for index.js or index.mjs or for file extensions.
src/index.js - Is this file actually necessary if only importing from subdirectories?
I don't think so, but you can keep it if you want.
Can I skip any type of build phase in the package?
Using the "exports" field does not require you to transpile your code.
The answer may depend on how you installed your components library. If you did it via either npm install <git-host>:<git-user>/<repo-name> or npm install <git repo url>,
You should be able to import {Button} from 'component-library/Button' as is, according to your first linked question. Similar to Node's require() resolution, Webpack should resolve subdirectories within component-library relative to component-library's entry point. You can find the docs on customizing the resolution behavior via the webpack.config.resolve property. material-ui seems to rely on resolving subdirectory imports from the module entry directory.
To distribute an ES module library, there's no need for building before distribution. However, projects such as create-react-app may need a pre-transpiled version.
Alternately, you can write import {Button} from 'components-library'.
Webpack will trace the dependencies back through each index without a fuss.
you have to install babel-plugin-module-resolver package
Specify the package relative path in your .babelrc file alias like this
{
"plugins": [
["module-resolver", {
"alias": {
"components-library": "./node_module/components-library"
}
}]
]
}
then you can import subdir of npm package like this
import { Button, ButtonGroup } from 'components-library/Button';
One of the possible solutions there is webpack aliasing system.
You can create another project, call it for example 'app-aliases', so your aliases will be reusable.
This project will has one js file with all of your packages paths:
const path = require('path');
module.exports = {
'#components': path.resolve(__dirname, 'node_modules/components-library/src'),
'#another': path.resolve(__dirname, 'node_modules/any/path/you/want'),
}
And then add it to the webpack configuration in any project which will be responsible for building/transpiling:
webpack.config.js
const appAliases = require('app-aliases');
const config = {
...
resolve: {
alias: {
...appAlises
}
}
}
In the runtime code you will be able to use it like this:
import {Button} from '#components/Button';
import {Something} from '#another'
If you are using typescript you will need to add the same aliases to the paths tsconfig property.
So answers to your questions are:
Yes, you can use any path in aliases
Yes, it is not necessary to build all of your projects
I see that now mui uses imports from directi packages (core for example), see https://material-ui.com/components/radio-buttons/ there is import Radio from '#material-ui/core/Radio';. But I hope they using re-export that I described below.
Also about node.js resolution mechanism.
When you import some library it tries to find node_modules/some-library/package.json and then main property inside it. This property should lead to your main entry point. Usually it is src/index.js (you should set it in package.json if it is no there yet). In this file you can re-export anything you want from internals file structure and will be able to use it without the full path.
Please see this repo for some examples.
I'am an angular developer never used react but what I could tell that material-ui are using monorepo where same concept exists in angular where we create one workspace and this workspace hold multiple project/packages as named in react. for more info Workspaces with Yarn
Material-ui using fake paths in tsconfig to make it appears like src folder doesn't exists this from the git you provided: tsconfig.json
This is possible but requires publishing a curated dist folder rather then the root of your project.
The whole thing is rather simple if you understand how module resolution works, and you just need a small script to prepare your distribution.
Lest I repeat all the details here, please see my answer for Importing from subfolders for a javascript package.