Exploring Modern FE Development Environments by Building a Coding Playground Part 2
2021. 2. 22.

(Updated 2022-03-04: Configuration code related to webpack-dev-server, babel, and jest has been modified.)
In Part 1, we built a basic JavaScript development environment with Lerna. While the basic setup might be sufficient for some, it usually isn't. Now, we'll add two more development environments: TypeScript and React.
First, we'll add the TypeScript environment. Before that, there's a task we need to do with Lerna. Since the TypeScript environment will ultimately be built on top of the JavaScript environment we've established, we'll make it possible to share dependency modules and extend the configuration files from the basic JavaScript environment. Then, we'll build the React development environment on top of the TypeScript environment. If you want to add only React on top of the basic JavaScript environment without TypeScript, you can skip the TypeScript-related steps.
Adding the pgts
package
Now, just like we did in Part 1, we use Lerna's create
command to add the TypeScript environment package, pgts
.
npx lerna create pgts
After completing the package setup, the packages/pgts
directory should be created. And, of course, Lerna will have created some directories. As we did at the beginning of Part 1, delete the __test__
directory and rename the lib
directory to src
. Also, rename the src/pgts.js
file to src/index.ts
.
We start with the same environment as pgjs
. Copy the contents of packages/pgjs/package.json
and overwrite pgts
's package.json
. Then, just change the name
to pgts
. It's good practice to modify the main
property and directories
as well, but they aren't crucial for our playground. If you're bothered by it, feel free to change the other properties too. Actually, you could just copy the contents of packages/pgjs
directly into packages/pgts
and only modify package.json
. There's no need to use Lerna's create
. We'll do it that way next time.
Hoisting devDependencies
to the root
We've added the pgts
package, but it's still empty except for package.json
. As mentioned earlier, the dependency modules of pgjs
will be shared with pgts
as they are common modules. Lerna makes this possible.
Lerna provides various features for managing multiple projects in a monorepo. The task we'll perform now is to move the dependency modules to the root so they can be accessed from there. This can be easily done with Lerna's link
command.
npx lerna link convert
When the link
command runs, the devDependencies
previously installed in node_modules
are moved to the root, and the contents of package.json
in each package and the root are modified. Check it out.
Now, the devDependencies
modules of pgjs
and pgts
are shared via the project root.
Conversely, devDependencies
that are not shared commonly can be installed as dependencies
to prevent them from being moved. Since we're creating a playground and have no intention of deploying, it doesn't matter. We'll just install everything as devDependencies
so all dependencies are shared.
Since all dependencies have been moved to the root by this process, the package-lock.json
files in the packages are no longer needed, so delete them.
Reducing configuration file duplication
Common modules can be shared from the root, but the pgts
environment still has no configuration files, so it can't do anything yet. Now we'll create the configuration files. Since pgjs
and pgts
use similar environments, their configuration files will likely have a lot of overlapping content. We'll try to reduce duplication as much as possible while setting things up.
Starting with ESLint configuration
ESLint configurations defined in .eslintrc.js
can be extended from other files using the extends
option. In Part 1, we configured ESLint in packages/pgjs/.eslintrc.js
. Now, we'll move this file to the packages
directory and make it usable by both pgjs
and pgts
. First, move the file. Then, create new .eslintrc.js
files in packages/pgjs
and packages/pgts
respectively, and enter the following:
module.exports = {
extends: '../.eslintrc.js', // Adjusted path
};
Actually, the pgjs
package will use the shared .eslintrc.js
moved to the packages
directory as is, so there's no need to create packages/pgjs/.eslintrc.js
separately. ESLint searches parent directories for configuration files if one isn't found, making it unnecessary. However, we created it for the future, in case specific rules need to be set separately. Now, based on the basic configuration, pgts
and pgjs
can add their own specific ESLint settings.
Next is Babel configuration
Babel configuration files are similar. Just like ESLint, you can extend configuration files using the extends
option. The usage is also identical. Those with quick hands might already be doing it. Copy the packages/pgjs/.babelrc
file to the packages
directory. Then, create new .babelrc
files in packages/pgjs
and packages/pgts
respectively, and enter the following:
{
"extends": "../.babelrc"
}
For now, both pgts
and pgjs
use the same environment, so the Babel configuration remains the same. Later, TypeScript settings will be added to the pgts
Babel configuration.
And finally, Webpack
Webpack doesn't provide an extension option like extends
. However, since the webpack.config.js
file is just a JavaScript file that needs to return a configuration object, extending it is easy. I created a file named packages/webpack.config.base.js
and defined a function within it that returns the basic configuration. Each package will then import and use this function. Let's try it.
First, create the packages/webpack.config.base.js
file, then copy and paste the contents of packages/pgjs/webpack.config.js
into it. This content will effectively become the base Webpack environment. However, since __dirname
will differ when imported externally, we'll add a parameter to receive the path and use it.
// ..
// Add dirname parameter, default to __dirname for standalone use if needed
module.exports = (env, argv, dirname) => {
// ..
entry: [], // Line 7: Modify to an empty array
// ..
path: path.resolve(dirname, 'dist'), // Line 10: Modify path resolution
// ..
'@src': path.resolve(dirname, 'src/'), // Line 26: Modify alias resolution
// ..
}
The entry point will differ for each package, so we make it an empty array in the base configuration. It will be added by the extending configuration.
Now that we've done this, let's create the Webpack configurations in packages/pgjs
and packages/pgts
that extend the base configuration. For pgjs
, we'll modify the existing webpack.config.js
file, and for pgts
, we'll need to add a webpack.config.js
file.
const baseConfig = require('../webpack.config.base');
module.exports = (env, argv) => {
// Pass __dirname to the base config function
let config = baseConfig(env, argv, __dirname);
// In `packages/pgts`, use `index.ts`
config.entry.push('./src/index.js');
return config;
};
They are almost identical, but in pgts
, the entry point file extension should be ts
, the TypeScript extension. Refer to the comment and make the change.
Shall we start the server to test? Before testing, we'll add an npm script to start the server. Because dependencies were linked with Lerna, we can't run Webpack directly with npx. Add the script to package.json
in both packages/pgts
and packages/pgjs
.
"scripts": {
"serve": "webpack serve --mode development",
"test": "jest"
},
That's all for reducing configuration file duplication. Actually, Jest is left, but like Webpack, Jest doesn't provide an option for configuration extension. However, similar to Webpack, the configuration file is just a plain JavaScript object, so it can be easily extended using the spread operator. Those who wish can do this separately. I'll pass.
Creating a TypeScript (typescript) development environment
So far, there's no difference between the pgts
and pgjs
packages. They are completely identical. Now, let's enable the pgts
package to use TypeScript. We'll perform three tasks:
- Configure Babel to understand TypeScript.
- Configure
tsconfig.json
. - Configure ESLint to understand TypeScript.
- Configure Jest to understand TypeScript.
Babel for TypeScript
Let's start with the first task: making Babel understand TypeScript.
Configuring Babel is also very easy. Navigate to the packages/pgts
directory and install the Babel TypeScript preset. This allows you to install the necessary plugins for using TypeScript with Babel all at once. After lerna link convert
, some local package dependencies need to be installed using npm
instead of lerna add
to install correctly. The exact reason isn't clear, but it seems to be an issue arising from shared node_modules
. I'll look into this when I have time. (If anyone knows, please let me know...). Since all local dependencies will eventually be moved to the root using link
, this is a temporary issue.
npm i -D @babel/preset-typescript
And we need to add it to the Babel configuration, right? Add the preset to the packages/pgts/.babelrc
file.
{
"extends": "../.babelrc",
"presets": [
"@babel/preset-typescript"
]
}
Now Babel can transpile TypeScript. Let's check. Modify packages/pgts/src/index.ts
as follows:
export function tsTest(a: number, b: number): number {
return a + b;
}
console.log(tsTest(1, 2));
Then start the server with npm run serve
. If the number 3 is printed in the console, it's successful. However, if the Babel configuration is incorrect, an error will occur when starting the server, so you'll know immediately.
We add export
beforehand to test it later with Jest.
tsconfig.json
Although we can now use TypeScript with Babel, it's still not enough. To fully utilize TypeScript's features, we need to create a tsconfig.json
file and configure TypeScript settings.
Currently, creating just one packages/pgts/tsconfig.json
would suffice, but since we plan to use TypeScript with React later, we'll create a default tsconfig.json
and extend it.
Add the packages/tsconfig.json
file (Note: the original Korean text mentioned pgtsconfig.json
, but the content suggests a base config in the packages
root).
{
"compilerOptions": {
"noEmit": true, // Do not emit output files (Babel handles transpilation)
"target": "ESNext", // Target latest ECMAScript features
"module": "CommonJS", // Module system for Node.js compatibility (Jest)
"strict": true, // Enable all strict type-checking options
"importHelpers": true, // Import tslib helpers
"moduleResolution": "node", // Module resolution strategy
"experimentalDecorators": true, // Enable experimental decorators
"esModuleInterop": true, // Allows default imports from CommonJS modules
"allowSyntheticDefaultImports": true, // Allow default imports from modules with no default export
"noImplicitAny": false, // Allow variables with an implicitly 'any' type (can be set to true for stricter checking)
"sourceMap": true, // Generate source maps
"lib": ["esnext", "dom", "dom.iterable"], // Standard library files to include
"types": ["node", "jest"], // Type definition files to include
"downlevelIteration": true // Provide full support for iterables in 'for-of', spread, and destructuring when targeting ES5 or ES3
},
"exclude": ["node_modules", "**/*.spec.ts", "**/*.test.ts"] // Exclude node_modules and test files from compilation check
}
This configuration should probably work as is without special modifications. If you encounter situations requiring changes, you can adjust it slightly according to the circumstances.
Then, create the configuration file packages/pgts/tsconfig.json
that extends packages/tsconfig.json
.
{
"extends": "../tsconfig.json", // Extend the base config
"compilerOptions": {
"baseUrl": "./", // Base directory for module resolution
"paths": {
"@src/*": [ // Path mapping for cleaner imports
"src/*"
]
}
},
"include": [ // Files to include in the TypeScript program
"jest.config.js",
".eslintrc.js",
"webpack.config.js",
"src/**/*.js", // Include JavaScript files in src
"src/**/*.ts", // Include TypeScript files in src
"src/**/*.tsx" // Include TSX files (for React later)
]
}
Here too, we used the extends
option to extend the configuration.
ESLint for TypeScript
ESLint also needs to be configured appropriately for TypeScript to perform static analysis correctly. Only then can this friend nag us properly. The setup is simple. First, install the necessary dependencies.
npm i -D @typescript-eslint/parser @typescript-eslint/eslint-plugin
Install the parser and plugin for TypeScript, then immediately modify the packages/pgts/.eslintrc.js
file.
module.exports = {
parser: '@typescript-eslint/parser', // Specify the TypeScript parser
extends: [
'../.eslintrc.js', // Extend the base ESLint config
'plugin:@typescript-eslint/recommended' // Use recommended TypeScript rules
],
plugins: ['@typescript-eslint'], // Enable the TypeScript plugin
parserOptions: {
ecmaFeatures: {
impliedStrict: true, // Enable global strict mode
},
project: './tsconfig.json', // Point ESLint to the tsconfig file for type-aware linting
tsconfigRootDir: __dirname, // Specify the root directory for tsconfig.json
},
};
It should probably work without major issues. If you want to verify, you can make some minor mischievous changes (?) to the index.ts
file and check if ESLint gives TypeScript warnings.
Jest for TypeScript
This time, let's configure Jest so it can also test TypeScript code. It follows the same pattern: Install necessary dependencies! Then configure!
Install! In packages/pgts
npm i -D ts-jest
Configure! packages/pgts/jest.config.js
module.exports = {
preset: 'ts-jest/presets/js-with-babel', // Use ts-jest preset that works with Babel
testEnvironment: 'jsdom', // Set the test environment to jsdom for browser-like environment
moduleFileExtensions: ['js', 'json', 'jsx', 'ts', 'tsx', 'node', 'd.ts'], // File extensions Jest should look for
moduleNameMapper: {
'^@src/(.*)$': '<rootDir>/src/$1', // Map @src alias to the src directory
},
globals: {
'ts-jest': {
babelConfig: true, // Tell ts-jest to use Babel configuration
},
},
watchPathIgnorePatterns: ['/node_modules/'], // Ignore node_modules when watching files
};
This configuration uses the TypeScript preset for Jest, specifically the preset that utilizes Babel.
Test Code! packages/pgts/src/index.test.ts
import { tsTest } from './index';
describe('tsTest', () => {
it('needs tests', () => {
expect(tsTest(1, 2)).toEqual(3);
});
});
Verify!
> npm test
PASS src/index.test.ts
tsTest
✓ needs tests (2 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: ...s
Ran all test suites.
You should probably have completed the tests successfully without any particular issues. However, if you open the index.test.ts
file, you'll likely see TypeScript warnings at the describe
and it
function locations. This happens because TypeScript doesn't know about the Jest APIs. Installing the Jest type definitions will resolve this.
npm i -D @types/jest
Now, the TypeScript environment setup is all finished.
Creating a React (React) development environment
We will build the React environment on top of the TypeScript environment. More accurately, it's a React development environment on top of a TypeScript environment, which is on top of a JavaScript environment. We are building it up step by step. Setting up the React environment is similar to creating pgts
from pgjs
. We will slightly modify the environments we are using to fit React. First, let's add the package.
Adding the react
package
For convenience, this time we won't use lerna create
to make the package. We'll just copy it.
Create the packages/pgreact
directory and copy everything from packages/pgts
except node_modules
. Don't forget to copy hidden files like .babelrc
.
After copying, although it's not strictly a problem, change the name
in package.json
to pgreact
.
Then, install dependencies with npm i
, try starting the server, and run the tests. It should probably work fine without issues, as it's currently identical to pgts
.
React installation and Babel configuration
Since we're creating a React development environment, we naturally need to install React. And since we need to use jsx
, we'll also configure Babel to recognize React.
First, let's install all the necessary dependencies at once.
Run this in packages/pgreact
:
npm i react react-dom core-js
npm i -D @babel/preset-react
(Note: react
and react-dom
are dependencies, core-js
might be needed for polyfills depending on Babel setup, and @babel/preset-react
is a dev dependency).
React can be used immediately after installation, but using jsx
requires additional steps because browsers don't understand jsx
. We'll configure Babel to transpile jsx
into valid JavaScript code. We've already installed the dependency, so we just need to configure it.
Add @babel/preset-react
to the presets
array option in .babelrc
. This means we'll use the preset that bundles necessary Babel plugins for React.
{
"extends": "../.babelrc",
"presets": [
"@babel/preset-typescript",
"@babel/preset-react" // Add this
]
}
Now, let's try using React. Change the extension of the packages/pgreact/src/index.ts
file to tsx
and create a simple React component in this file.
import React from 'react';
import ReactDOM from 'react-dom';
const Hello: React.FC = () => {
return <h1>Hello World</h1>;
};
const container = document.createElement('div');
document.body.appendChild(container);
ReactDOM.render(<Hello />, container); // Render into a container
Since we changed the entry point file extension, update it in the packages/pgreact/webpack.config.js
configuration as well.
// …
// Change ts to tsx
config.entry.push('./src/index.tsx');
// …
Now it seems like we can start the server, but not yet. Because we're integrating Babel through Webpack, and we haven't told the Webpack babel-loader to process tsx
files yet. Let's modify that part. This setting is in packages/webpack.config.base.js
.
//…
module: {
rules: [
{
// Modify this regex to include tsx and jsx
test: /\.(ts|tsx|js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
//…
And one more thing, declare that we'll use jsx
in packages/pgreact/tsconfig.json
.
{
// …
"compilerOptions": {
"baseUrl": "./",
"paths": {
// ... paths should exist here from pgts/tsconfig.json
"@src/*": [
"src/*"
]
},
"jsx": "react-jsx" // Add this (or "react" for older versions)
},
// Ensure include covers tsx
"include": [
"jest.config.js",
".eslintrc.js",
"webpack.config.js",
"src/**/*.js",
"src/**/*.ts",
"src/**/*.tsx"
]
//…
}
(Note: Added react-jsx
which is common for modern React setups, and ensured paths and include are present).
Not doing this won't cause major problems, but TypeScript keeps warning, which is annoying.
Now, start the server to check. "Hello World" should appear, right? If not, glare back at the steps above.
ESLint for React
Now that the basic environment is set up, let's ask ESLint to monitor our React code as well. It helps find potential mistakes in the code and provides various nags, making ESLint essential for React development too. First, let's install the ESLint plugins.
npm i -D eslint-plugin-react eslint-plugin-react-hooks @types/react @types/react-dom
We installed React type declarations and the React Hooks plugin together. Whatever you say, using Hooks is better. I think it's just a matter of familiarity. Once the plugin installation is complete, add the plugins to the configuration.
packages/pgreact/.eslintrc.js
module.exports = {
extends: [
'../.eslintrc.js', // Base config
'plugin:@typescript-eslint/recommended', // TS rules (should inherit from pgts base ideally)
'plugin:react/recommended', // Add React recommended rules
'plugin:react-hooks/recommended', // Add React Hooks rules
],
plugins: [
'@typescript-eslint', // TS plugin (should inherit)
'react', // Add react plugin
'react-hooks' // Add react-hooks plugin
],
settings: { // Add settings section
react: {
version: 'detect', // Automatically detect React version
},
},
// Ensure parser and parserOptions are correctly set for TSX
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true, // Enable JSX parsing
impliedStrict: true,
},
ecmaVersion: 2021, // Or newer
sourceType: 'module',
project: './tsconfig.json',
tsconfigRootDir: __dirname,
},
// Add rules if needed, e.g., disabling prop-types if using TypeScript
rules: {
'react/prop-types': 'off'
}
};
(Note: Made adjustments to ensure TS/React settings integrate properly and added react/prop-types: off
as it's common with TypeScript).
This completes the ESLint setup. Shall we check if it's configured correctly? Let's add code using hooks to the Hello
component in index.tsx
. If you change the code as below, a react-hooks/exhaustive-deps
warning should appear if the setup is correct.
import React, { useState, useEffect } from 'react';
// ... rest of imports
const Hello: React.FC = () => {
const [a, /*setA*/] = useState(0); // Added state, commented out setter to avoid unused var warning
useEffect(() => {
console.log(a);
}, []); // Warning appears here
return <h1>Hello World</h1>;
};
// ... rest of file (ReactDOM.render)
Just check and revert the changes.
Jest for React
Although Jest can be used for E2E tests, here we only consider unit tests. Since Jest is already installed, we can write test code immediately. However, for testing convenience, we'll add a utility tool. Its name is Testing Library (testing-library). It provides useful functions for writing front-end test code, enabling quick and easy test writing. It supports not only React but also various frameworks like Vue, Angular, and even the DOM. Unless there's a specific reason not to, its use is recommended.
npm i -D @testing-library/react @testing-library/jest-dom
(Note: Added @testing-library/jest-dom
for useful matchers).
The Jest configuration already considers React, so there's no need to touch the settings separately. We might need to setup jest-dom
matchers globally. Let's add a setup file.
Create packages/pgreact/jest.setup.js
:
import '@testing-library/jest-dom';
Modify packages/pgreact/jest.config.js
:
module.exports = {
// ... existing config
preset: 'ts-jest/presets/js-with-babel',
testEnvironment: 'jsdom',
moduleFileExtensions: ['js', 'json', 'jsx', 'ts', 'tsx', 'node', 'd.ts'],
moduleNameMapper: {
'^@src/(.*)$': '<rootDir>/src/$1',
},
globals: {
'ts-jest': {
babelConfig: true,
},
},
watchPathIgnorePatterns: ['/node_modules/'],
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'], // Add this line
};
Now, let's write a simple test code for the Hello
component. Currently, the Hello
component is defined in the entry point file, so let's first separate it into its own file.
packages/pgreact/src/hello.tsx
import React from 'react';
const Hello: React.FC = () => {
return <h1>Hello World</h1>;
};
export default Hello;
The index.tsx
file now uses the separated Hello
component.
import React from 'react';
import ReactDOM from 'react-dom';
import Hello from './hello'; // Import the component
const container = document.createElement('div');
document.body.appendChild(container);
ReactDOM.render(<Hello />, container);
Rename the packages/pgreact/src/index.test.ts
file extension to tsx
. This way, TypeScript won't warn even if we use jsx
. Then, write a simple test code for the Hello
component.
packages/pgreact/src/hello.test.tsx
(renamed from index.test.ts)
import React from 'react';
import { render, screen } from '@testing-library/react'; // Import screen
import Hello from './hello';
describe('Component test', () => {
it('renders hello world heading', () => { // More descriptive test name
render(<Hello />); // Render the component
// Use screen queries - preferred way
const headingElement = screen.getByText('Hello World');
expect(headingElement).toBeInTheDocument(); // Check if element exists
expect(headingElement.nodeName).toEqual('H1'); // Check if it's an H1
});
});
(Note: Renamed file, used screen
from testing-library, improved test description and assertion).
Using Testing Library's render
function, we render the Hello
component into a virtual DOM. Then, using the getByText
function (via screen
), we find the node in the currently rendered DOM that contains the text "Hello World". Finally, we verify if the found node is indeed an H1
.
Since the Hello
component is very simple and lacks logic, we wrote this kind of test code. However, in situations without logic that modifies elements, testing the node name is meaningless. Such simple rendering doesn't even need testing, and if you must, use snapshots. Also, checking the element's name is often a meaningless test. Element structure can change, and whether the node structure itself is truly important... blah blah blah... Again, it's turning into a "test sermon," so I'll cut the unnecessary rambling.
Styled Components (styled-component)
There have been many attempts to integrate CSS into JavaScript code or framework components. I've reviewed a few options myself and currently settled on Styled Components. Tools like this can actually be replaced with others at any time. Trying different approaches for each project could be interesting. Anyway, since I've been using Styled Components recently, I'll install it as the final step for the React environment and wrap up.
npm i styled-components
npm i -D @types/styled-components babel-plugin-styled-components
(Note: Added types and babel plugin for better DX/SSR).
Styled Components run under JavaScript syntax, so you can use them immediately after installation. Shall we modify index.tsx
? (Actually, let's modify hello.tsx
to style the component itself).
Modify packages/pgreact/.babelrc
to include the plugin:
{
"extends": "../.babelrc",
"presets": [
"@babel/preset-typescript",
"@babel/preset-react"
],
"plugins": [ // Add plugins section
"babel-plugin-styled-components"
]
}
Modify packages/pgreact/src/hello.tsx
:
import React from 'react';
import styled from 'styled-components'; // Import styled
// Define the styled component
const Title = styled.h1`
color: blue;
`;
// Use the styled component instead of a regular h1
const Hello: React.FC = () => {
return <Title>Hello World</Title>;
};
export default Hello;
(Note: Modified hello.tsx
instead of index.tsx
for better component encapsulation).
We added a styled component named Title
and applied color using CSS. When first applying Styled Components, the different workflow compared to traditional CSS can be confusing. A broad but simple tip: think of each styled component as a CSS class. This mindset might help a bit when structuring.
lerna link convert
once more
Since there are also duplicated dependencies in ts
and react
, these too should be moved to the root and shared by everyone from the root node_modules
.
We did this at the beginning, right? One command is all it takes.
npx lerna link convert
(Note: The original text used lerna convert link
, but lerna link convert
is the correct command shown earlier).
If you check package.json
, you can see that only the dependencies needed exclusively for the React environment remain, and everything else has moved to the root. Now you can save hard drive space even while creating tens of prototype or experimental projects :)
Utilizing the playground
Alright, the environment setup I needed is complete for now. Previously, when watching development-related YouTube videos, if I wanted to try out the example code, I'd open the editor but then get discouraged by the hassle of setting up the necessary environment and just decide to watch. Now, I can simply copy the required environment, give it a new name, and immediately experiment in a comfortable editor setting. For example, if the needed environment is React, I can create a directory like packages/mytest
, copy the contents of packages/pgreact
straight into it, and start typing the experimental code right away.
I use the same method for contents or prototypes I want to experiment with in an isolated environment during work. Not just for simple tests, but even somewhat serious project prototyping starts here. I create a directory for each project, copy the environment, and then manage it using a separate Git branch. This keeps the main branch clean, maintaining only the environment packages.
Another worthwhile task involves the packages
option in lerna.json
, which can accept an array of multiple paths. By adding paths like apps/*
, for instance, you can separate the packages containing only environment configurations from the projects using those environments for experimentation. This could make management easier. I didn't cover it in this post because it would unnecessarily lengthen the content with path modifications for basic environment settings, but I plan to apply it to the shared repository soon.
One drawback is the need for a feature like "Export" to make a single project independent, but implementing that involves a lot of work. In such situations, you still have to move things manually, step by step.
Another way to utilize
Developers creating open-source projects or libraries often test their work in various supported environments at least once before deployment. For open-source libraries, vanilla JavaScript is a given, and it must also run well in ESM and TypeScript environments with clear typings. If support extends to framework components like React, Vue, and Angular, the number of usage environments to test can become overwhelmingly large. In such cases, pre-configuring each environment in the playground and pulling it out for testing whenever needed makes managing each environment easier and the testing itself much more convenient. I use it this way too. Furthermore, even modules not yet published to NPM can be used as dependencies and tested as if they were published, using Lerna's bootstrap
command with local modules. This is also one of Lerna's strengths.
Wrapping up and one missing piece
So, we've reached the end of creating a playground where we can easily add simple projects anytime.
Actually, one stack is missing compared to the initial plan: Storybook. If projects were managed per environment using Storybook, you could add stories whenever needed for independent experiments, right? It also provides a gallery-style UI, making management easy. With Storybook, there would be no need to copy directories when a specific environment is needed. The directory copying method is an unavoidable temporary measure to fill the gap left by Storybook's absence. The playground initially envisioned was completed with Storybook. The reason Storybook was omitted is that it didn't support Webpack 5 at the time. It had to be excluded. Webpack 5 was facing many difficulties due to third-party migration issues. Someday, when Storybook supports Webpack 5, I will write Part 3 to conclude. For now, we have to rely on directory copying.
Anyway, using the playground as a pretext, we've touched upon the essential elements of the modern FE development environment one by one. Depending on the project being developed, many more tools might need to be added, but most will likely be built on top of this environment. They are like sturdy pillars.
There's a possibility of changes or additions next year. Several tools are currently being considered. If the opportunity arises next year, I will introduce them then.
The completed hatchery repository is here.
with kakaopay
Recommend Post
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.