# Upgrade Guide
# 1.0.0
# Latest CLI update adds seed command
Nodewood CLI v0.17.0 added the seed command, which can run seed files to pre-populate your database with known-good records. This is mostly useful for development, when you are standing up a new Postgres container or want to wipe everything and start fresh. New projects created from now on will have all the required files in place, but existing projects will require a few changes to enable this command.
First, add the following entry to the scripts object in your root package.json:
"seed": "knex seed:run",
Next, add the following entry to your knexfile.js, in the development object:
module.exports = {
development: {
// ...
seeds: {
directory: './app/seeds/development',
},
},
test: {
// ...
Finally, add a seed file. By default, seeds for development are located in app/seeds/development, but you can change that when modifying your knexfile, as above. A sample seed file follows:
exports.seed = async (knex) => {
await knex('users').del();
await knex('users').insert([
{
id: 1,
created_at: '2022-01-01 00:09:48.856518+00',
updated_at: '2022-01-01 00:09:48.856518+00',
name: 'User One',
email: 'one@user.com',
email_confirmed: true,
password: '$2a$10$4A7ATOTcApZ3kCjfSJE.lOnWH..OMISwxx5lrsH3D.aGfmwI5YldG',
account_type: 'admin',
last_logged_in_at: '2022-01-01 00:09:48+00',
flags: '{}',
jwt_series: 1,
secure_flags: '{}',
},
{
id: 2,
created_at: '2022-01-02 19:49:47.041227+00',
updated_at: '2022-01-02 19:49:47.041227+00',
name: 'User Two',
email: 'two@user.com',
email_confirmed: true,
password: '$2a$10$.tVfraKR9tSZejKuIpEY5OBMRlWEzTNmIenFi.adgki8hcm/MJq0a',
account_type: 'user',
last_logged_in_at: '2022-01-02 20:23:42+00',
flags: '{}',
jwt_series: 1,
secure_flags: '{}',
},
]);
await knex.schema.raw('ALTER SEQUENCE users_id_seq RESTART WITH 3');
await knex('teams').del();
await knex('teams').insert([
{
id: 1,
created_at: '2022-01-01 00:09:48.856518+00',
updated_at: '2022-01-01 00:09:48.856518+00',
name: 'Team One',
currency: 'usd',
stripe_customer_id: null,
flags: '{}',
secure_flags: '{}',
},
{
id: 2,
created_at: '2022-01-02 19:49:47.041227+00',
updated_at: '2022-01-02 19:49:47.041227+00',
name: 'Team Two',
currency: 'usd',
stripe_customer_id: null,
flags: '{}',
secure_flags: '{}',
},
]);
await knex.schema.raw('ALTER SEQUENCE teams_id_seq RESTART WITH 3');
await knex('users_teams').del();
await knex('users_teams').insert([
{
user_id: 1,
team_id: 1,
created_at: '2022-01-01 00:09:48.856518+00',
updated_at: '2022-01-01 00:09:48.856518+00',
role: 'owner',
},
{
user_id: 2,
team_id: 2,
created_at: '2022-01-02 19:49:47.041227+00',
updated_at: '2022-01-02 19:49:47.041227+00',
role: 'owner',
},
]);
};
This file will create two users on their own separate team, with no subscriptions. Both users' passwords are simply "password". You can add, remove, and modify from this as you choose to create a reasonable starting template for development.
# Positioning styling of UserMenu moved.
The AdminTemplate/UserMenu and AppTemplate/UserMenu components have had their margin-based positioning styling removed, and instead added to the template they're included in. This is where said positioning should have been to start, honestly. This will only affect you if you've customized your AppTemplate or AdminTemplate, in which case you can re-add the missing mt-4 mr-6 classes to the component in those files.
# Services are now automatically injected into Controllers and other Services.
Services commonly use other services to accomplish things. (For example, the SubscriptionsService calls out to the UsersService to send mail.) This is a good way to keep related code grouped together and create "building blocks" that can be re-used later.
One problem you can run into though, is circular dependencies - if AbcService wants to call a function from XyzService, and XyzService also wants to make a call in AbcService, they both try to require() each other, and Node will error out.
To solve this, all Services are now loaded at application start and injected into every Controller and Service automatically. All of your calls to this.abcService.doThing() will continue to work, but you will want to go and ensure you remove every instance of:
this.abcService = new AbcService({ db })
Services created in this way will not have other services automatically injected into them, which can brings you back around again to circular dependency, if you're not careful.
You will also need to modify the constructor of your existing Controllers to look like this:
/**
* Constructor.
*
* @param {MassiveJS} db - The MassiveJS db connection.
* @param {Mailer} mailer - The mailer.
* @param {Array<Service>} services - The services to inject into this controller.
*/
constructor({ db, mailer, services } = {}) {
super({ db, mailer, services });
Note that services has been added as parameter for the constructor and passed along to the super() call. This will ensure all the services are injected into the Controller.
# keys parameter in Service's update() and insert() is now optional.
As a safety measure, service update() and insert() functions required you to pass a value object used to update or insert, as well as an array of keys that would be used to restrict the value object. This was great when using a request body, for example, because it allowed you to really simplify your code (pass the entire body) while keeping it secure (only accepting certain values).
But if you were writing trusted code (for example, updating one table based solely on values from another), this forced you to do something like:
this.abcService.insert(tx, newValues, Object.keys(newValues));
This is fine, but it's overly-verbose and unnecessary. Going forward, if the keys parameter is missing, all values from the values parameter are used without restriction.
This doesn't require any mandatory changes to your code, but if you do have any code similar to the above, you can safely remove the final parameter there and have slightly less-verbose code.
# Healthcheck testing
The docker-compose file now specifies healthchecks for its services, so that dependent services are always running before starting services with dependencies. This necessitated adding a healthcheck endpoint to the API service, at /api/public/healthcheck.
If nodewood dev seems to be taking a long time, it is likely because this healthcheck is not working correctly, which is likely because you have customized the features.wood config property in app/config/app.js. Make sure that the healthcheck feature is enabled here, and it should resolve the issue.
You can also use this healthcheck for your own purposes, such as site uptime measuring.
# Pulumi deploy system added
If you already have a deploy system set up and working for your app, this likely won't concern you. But if you have yet to deploy your app to production, this provides another option.
Pulumi (opens new window) is an infrastructure-as-code system designed to make it easy and reliable to stand up and modify cloud infrastructure for your projects. The Nodewood implementation of Pulumi will set up and deploy your application to AWS, using RDS Postgres databases and Fargate for application image serving.
For more information, check out the Pulumi section in Deploying to Production.
# 0.20.1
# Nodewood CLI 0.16.2 now required
The add command in the Nodewood CLI has been modified so that the feature parameter is never pluralized. This has been the source of a bunch of weird behaviours in the past, and it is simpler both in the code and conceptually to just accept the feature name as given, and only appropriately pluralize the generated files, not the feature folder they're added to.
This requires updating your Nodewood version to 0.20.1, which has updated template files that expect this behaviour.
Some examples of how this command will behave after the update:
> nodewood add:feature dog
Generated controller: /app/features/dog/api/controllers/DogsController.js
Generated model: /app/features/dog/lib/models/DogModel.js
> nodewood add:feature box --plural=boxen
Generated controller: /app/features/box/api/controllers/BoxenController.js
Generated model: /app/features/box/lib/models/BoxModel.js
> nodewood add:controller box label
Generated controller: /app/features/box/api/controllers/LabelsController.js
> nodewood add:model box label
Generated model: /app/features/box/lib/models/LabelModel.js
This behaviour is much more consistent and easier to predict.
To update your Nodewood CLI tool, run: yarn global add @nodewood/cli.
# 0.20.0
# Vue 2 compatibility mode disabled
The Vue 2 compatibility build has been disabled with this release, meaning Nodewood now runs on standard Vue 3 code. If you haven't completed your conversion to Vue 3 yet and are now getting errors you cannot trace down to fix, you can re-enable compatibility mode with the following steps:
- In
app/package.jsonandwood/package.json, add"@vue/compat": "^3.2.0",to your dependencies. - In
wood/vue.config.js, add the following inside themodule.exportsobject:
chainWebpack: (config) => {
config.resolve.alias.set('vue', '@vue/compat');
config.module
.rule('vue')
.use('vue-loader')
.tap((options) => ({
...options,
compilerOptions: { compatConfig: { MODE: 2 } },
}));
},
- In
wood/ui/main.js, change the first line toimport { createApp, configureCompat } from 'vue';, and immediately after the imports section, add the following:
configureCompat({ INSTANCE_ATTRS_CLASS_STYLE: false });
# Script runner added
You can now write and run scripts from the command-line. However, you'll need to add a new file in your app folder for this to work correctly. Create an app/cli folder, and inside of it, create a script.js file containing the following:
const { runScript } = require('../../wood/cli/script');
runScript();
You'll also need to update your ESLint configuration, so it knows about the root #cli folder. Update settings.import/resolver.alias-array.map in .eslintrc.js:
map: [
['#api', [
resolve(__dirname, 'app/api'),
resolve(__dirname, 'wood/api'),
]],
['#cli', [
resolve(__dirname, 'app/cli'),
resolve(__dirname, 'wood/cli'),
]],
['#config', [
resolve(__dirname, 'app/config'),
resolve(__dirname, 'wood/config'),
]],
['#features', [
resolve(__dirname, 'app/features'),
resolve(__dirname, 'wood/features'),
]],
['#lib', [
resolve(__dirname, 'app/lib'),
resolve(__dirname, 'wood/lib'),
]],
['#ui', [
resolve(__dirname, 'app/ui'),
resolve(__dirname, 'wood/ui'),
]],
['@app', resolve(__dirname, 'app')],
['@wood', resolve(__dirname, 'wood')],
],
# Admin Dashboard modified to use rollup table, DashboardRollupScript added
The Admin Dashboard used to use a temporary calculation to tell you how many users/teams and MRR you had, and compared it to the start of the month. As of 0.20.0, these calculations come instead from a rollup table (admin_dashboard_rollups).
You will need to make sure you run migrations (nodewood migrate), and then run the script, with either nodewood script features/admin/cli/scripts/DashboardRollupScript in development, or node app/cli/script.js features/admin/cli/scripts/DashboardRollupScript in production. Additionally, you likely want to run the DashboardRollupScript daily via cron in production, to ensure that the data is always up-to-date.
# Adds model name to FIELDS definition
It's very common to be working with multiple Models in a single file, and you may need to access multiple Model FIELDS as well. Since those are all currently exported with the same name, it falls to you to destructure them as a different name in such cases.
To fix that, we've renamed all instances of model FIELDS to NAME_OF_MODEL_FIELDS. If you import these model fields anywhere in your app, you'll need to rename that field as well.
# Adds validator name to FORM_FIELDS definition
Similarly, every Validator exported a FORM_FIELDS object, which could get tricky when you wanted to validate multiple things in a single file.
To fix that, we've renamed all instances of validator FORM_FIELDS to NAME_OF_VALIDATOR_FORM_FIELDS. If you import these validator fields anywhere in your app, you'll need to rename that field as well.
# Package upgrades
Many core packages were upgraded. Make sure to review the changes to your app/package.json and ensure your app compiles and runs correctly.
You will need to remove the following packages from your app/package.json:
@vue/compiler-sfc@vue/eslint-config-airbnb
You will need to update the following packages in your app/package.json:
"@vue/cli-plugin-unit-jest": "^5.0.0-rc.1"
You will need to add the following line to the exports of your root jest.config.js:
testEnvironment: 'node',
You will need to update any usage of loadable in Composition API components to also pass the current instance as a parameter:
import { ref, getCurrentInstance } from 'vue'; // Add `getCurrentInstance` to this import
// ...
const loadingFunction = loadable(
(values) => store.dispatch('StoreName/loadingFunction', values),
'loadingFunction',
getCurrentInstance(),
);
You will need to add the following lines to the rules section of your root .eslintrc.js:
'vuejs-accessibility/click-events-have-key-events': 'off', // Disabled until a11y review
'vuejs-accessibility/mouse-events-have-key-events': 'off', // Disabled until a11y review
'vuejs-accessibility/no-autofocus': 'off', // Disabled until a11y review
After completing these steps, run yarn install and restart your Docker containers. If you get any strange errors, try deleting your node_modules folder and re-running yarn install.
# Troubleshooting
- Getting strange build errors from your UI container?
Delete all yournode_modules(make sure to get the ones inappandwoodas well), and restart your containers. - Missing tokens from account creation and password reset emails?
Make sure to run migrations, then restart your containers.
# 0.19.0
# Rename UI config values
The following options in the ui config file have been renamed:
appSidebarrenamed toappMenuEntriesadminSidebarrenamed toadminMenuEntriesdropdownrenamed touserMenuEntries
If you have extended this file, make sure to rename these options in your file as well.
# Switch to DataTable component
A new DataTable component has been added that handles the vast majority of cases where you will want to display tabular data loaded from a controller. For examples of how to use it, review the documentation.
It is recommended you switch to using this DataTable component in most cases, since it vastly reduces the amount of code necessary to display tables, and it comes pre-styled to display correctly in both desktop and mobile mode.
# 0.18.0
# Vue 3 - General
This release upgrades Vue from 2.6 to 3.2. Vue 3 in Nodewood is configured in "compatibility mode", which gives you time to convert your UI components into a form that is supported by Vue 3 (and for the Vue modules that Nodewood relies on to be converted as well).
To enable your project to work in Vue 3's compatibility mode, you must do the following:
- In your root
package.json:- Update
eslintto"eslint": "^7.19.0".
- Update
- In
app/package.json:- Add the following to your
dependencies:"@vue/compat": "^3.2.0""@vue/compiler-sfc": "^3.2.0"
- Update the following in your
dependencies:"vue": "^3.2.0","vue-router": "^4.0.10","vuex": "^4.0.2"
- Remove the following from your
dependencies:vue-template-compiler
- Add the following to your
- In your root
.eslintrc.js:- Add the following section (or add these entries to your existing
globalssection):
globals: { // Vue 3 compiler macros defineProps: 'readonly', defineEmits: 'readonly', defineExpose: 'readonly', withDefaults: 'readonly', },- Add the following to the
rulessection:
'vue/no-v-for-template-key': 'off', // Conflicts with vue3 'import/no-extraneous-dependencies': 'off', // Conflicts with wood folder imports- Change
'plugin:vue/recommended'to'plugin:vue/vue3-recommended'.
- Add the following section (or add these entries to your existing
- If you have added a
chainWebpackentry in yourapp/vue.config.js, you must make sure it includes the following:
chainWebpack: (config) => {
config.resolve.alias.set('vue', '@vue/compat');
config.module
.rule('vue')
.use('vue-loader')
.tap((options) => ({
...options,
compilerOptions: { compatConfig: { MODE: 2 } },
}));
},
- Run
yarn install. - Finally, update your
app/ui/main.jsto the following:
import app from '@wood/ui/main';
export default app;
If you have customized your app's main.js, you'll have to add those customizations back in, but this is much easier now due to new functions in #ui/configure that can be extended or overridden.
# Vue 3 - Stores
The application initialization has been changed to be a synchronous process, so store initialization must also be changed to be synchronous. You will need to change the initStores function in your ui/init.js files to look like the following:
/* eslint-disable global-require */
store.registerModule('Example', require('#features/examples/ui/stores/ExampleStore'));
// DO NOT REMOVE: Generated stores will be added above this line
/* eslint-enable */
# Vue 3 - Routes
Instead of adding routes to a router object, routes are collected into an array and passed into the router object at creation. This function is no longer asynchronous, either, as application initialization has changed to be synchronous instead. This means that the initRoutes function in your ui/init.js files will need to change to look like the following:
/**
* Initialize Vue routes for this feature.
*
* @return {Array}
*/
initRoutes() {
const routes = [];
routes.push({
path: '/example',
name: 'example',
component: () => import(/* webpackChunkName: "example" */ '#features/examples/ui/pages/ExamplePage'),
});
// DO NOT REMOVE: Generated routes will be added above this line
return routes;
}
# Vue 3 - Charts
vue-chartjs is not Vue 3-compatible and likely will not be updated to be so, so we have switched to vue-chart-3. It can be used mostly the same way, but you may need to consult their documentation (opens new window).
Once your charts have been converted to vue-chart-3, make sure to remove vue-chartjs from your app/package.json.
# Vue 3 - Adding PrimeVue
This release adds the PrimeVue component library (opens new window), primarily to replace vue-js-modal (which looks likely that it will not be updated to work with Vue 3). In order to enable this, you will need to create a postcss.config.js file in your project root:
module.exports = {
plugins: [
require('autoprefixer'), // eslint-disable-line global-require, import/no-extraneous-dependencies
],
};
# Vue 3 - Dialog component changed to PrimeVue Dialog
The Dialog component Nodewood was using (vue-js-modal) is stuck on Vue 2, and is likely not going to be updated to be compatible with Vue 3. As a result, in this release, we have changed all the Nodewood dialogues to use the PrimeVue Dialog component, and you will need to do so as well.
First, you will need to add a visible entry in your data entry for your dialog component:
data: () => ({
visible: false,
}),
Next, you'll need to replace your <modal> tag with a <Dialog> tag:
<Dialog
:visible="visible"
:closable="false"
header="Edit User"
>
The header property replaces the contents of the h2.dialog-title, and by default, all PrimeVue Dialogs come with a close button. To retain the style Nodewood dialogs had before, set closable to false. The visible property controls whether the dialog is shown or hidden, so you'll need to change your openDialog() and closeDialog() functions:
openDialog() {
this.visible = true;
},
closeDialog() {
this.visible = false;
},
Styling dialogs is now much easier! You can set the :style property on your Dialog component and style it directly with CSS, and use :breakpoints to add responsive styles. For a dialog that starts at 50% width, but switches to 100% width when the viewport is less than 640px, you can do the following:
<Dialog
:visible="visible"
:style="{ width: '50vw' }"
:breakpoints="{'640px': '100vw'}"
:closable="false"
header="Edit User"
>
Finally, make sure to remove vue-js-modal from your app/package.json file, to keep your bundle size small.
# Vue 3 - Toast component changed to PrimeVue Toast
vue-toasted (opens new window) has been removed, since it is only Vue 2-compatible and looks like it will not be updated for 3. Instead, it has been replaced with [PrimeVue Toasts](https://www.primefaces.org/primevue/showcase/#/toast, easily accessible through helper functions in #ui/lib/toast.js:
import errorToast from '#ui/lib/toast';
// ...
errorToast('Credit card invalid.');
The toast title and other options can be passed in as additional parameters to further customize the toast.
Once finished, make sure to remove vue-toasted from your app/package.json and run yarn install.
# Vue 3 - vue-loadable removed, replaced with vue-is-loading
vue-loadable (opens new window) has been removed, since it is only Vue 2-compatible and looks like it will not be updated for Vue 3. Instead, it has been replaced with vue-is-loading (opens new window), a Vue 3 javascript port. This is a drop-in port, and can be used as before.
If using Vue 3's Composition API (opens new window), you can use vue-is-loading's loadable function directly, like so:
<template>
<div>
<loading-spinner v-if="$isLoading('saveUser')" />
<button @click="submit">
Submit
</button>
</div>
</template>
<script setup>
import { ref, getCurrentInstance } from 'vue';
import { loadable } from 'vue-is-loading';
import LoadingSpinner from '#ui/components/LoadingSpinner';
const name = ref('');
const saveUser = loadable(
(values) => store.dispatch('Users/saveUser', values),
'saveUser',
getCurrentInstance(),
);
const submit = async () => {
await saveUser({ name });
};
</script>
Once finished, make sure to remove vue-loadable from your app/package.json and add "vue-is-loading": "1.0.0",, then run yarn install.
# Vue 3 - v-click-outside removed, replaced with click-outside-vue3
v-click-outside (opens new window) has been removed, since it is only Vue 2-compatible and looks like it will not be updated for Vue 3. Instead, it has been replaced with click-outside-vue3 (opens new window), a Vue 3 port. This is a drop-in port, and can be used as before.
You shouldn't need to make any changes to your code, as this directive is added in wood and shouldn't actually be referenced anywhere in app.
# Vue 3 - Final migration notes
From here, you will have to upgrade your own code from Vue 2 code to Vue 3 code (including any file generated by nodewood add). Lint messages and console warnings should indicate fairly clearly what changes you need to make (searching for the specific lint rule in question will give you examples for how to convert from "bad" code to "good" code), but the Vue docs have a more in-depth guide to migration (opens new window), if you have more advanced issues.
You may also need to update or switch libraries to Vue 3 compatible versions.
If you get stuck, feel free to email hello@nodewood.com with the details of the issue you're experiencing.
# Vue 3 - Troubleshooting
This dependency was not found: "vue"
Delete yarn.lock and run yarn install to get a fresh copy of the trouble libraries.
Syntax Error: Error: No PostCSS Config found in:
Make sure the add the postcss.config.js to your root folder as mentioned in the "Adding PrimeVue" section.
- Miscellaneous linter errors
If you have new and persistent linter errors when working on your components or trying to build the UI, make sure to refer closely to this guide (especially old upgrade steps, if you are upgrading from anything but the most recent version) and ensure especially that all packages with eslint in them match the versions specified here.
# Styling
Some Tailwind styles have been simplified so that they behave more predictably across different browsers. A side effect of this is that your layout will completely blow up unless you modify your app/ui/public/index.html file and change the line <div id="app"></div> to <div id="app" class="w-full"></div>.
# 0.17.0
# CRITICAL: BACK UP YOUR DATABASE!
The migration in this release will create new teams and users_teams tables, and modifies the subscriptions table to be connected to the teams table instead of the users table. To do this, it creates teams and users_teams entries for every entry in users.
This process should be pretty safe and automatic! But if you've made any changes to your users table, there could be unexpected side-effects, so BACK UP YOUR DATABSE BEFORE APPLYING ANY MIGRATIONS FROM THIS RELEASE.
This goes double for any production databases you apply this to.
# New config files
Because of some upgrades made and how webpack works, you will need to create config files in app/config for all config files in wood/config or you will see "Cannot find module ./x" error messages in your browser console. These files can be effectively empty, just inheriting defaults from their "wood" counterparts, like so:
const woodConfig = require('@wood/config/teams');
/**
* @type {Object} Application teams config values.
*/
module.exports = {
/**
* Start with default teams config.
*/
...woodConfig,
// Overwrite default configs here
};
# Updating your email configuration
The email transport configuration has been moved to its own file, so that you can import email address configuration values safely from the UI without needing to add email-sending libraries to your UI package, needlessly increasing file size.
To move your configuration, run nodewood eject config-api/email, then move the transportConfig value from your existing config/email entry to config-api/email, ensuring you also move the necessary require statements with it.
# Updating tests
The COOKIE_JWT_USER_ID_1 constant has been renamed to COOKIE_JWT_OWNER_ID_1 to reflect the fact that that it now contains an Owner role. (An COOKIE_JWT_OWNER_ID_1 constant representing a user with a member role has also been added.) You will need to use find and replace to rename this constant through your tests.
# Updating .env file
Add an entry for GENERAL_HASHID_SALT=generalHashidSalt, where generalHashidSalt should be a random series of characters that will be used to salt general-purpose Hashids created in your project. This should be different from your JWT_HASHID_SALT entry so that users cannot examine their JWTs and compare Hashids generated there and elsewhere and potentially glean information about your user IDs or other sequential values.
# ActiveUser
The Users store was renamed to the ActiveUser store for clarity. Now, instead of referencing Users.current alongside Users.role, Users.subscription, and Users.team, you reference ActiveUser.user, ActiveUser.role, ActiveUser.subscription and ActiveUser.team.
You will need to update your Vue files, so that anywhere you were referencing the Users store, you are now referencing the ActiveUser store, and anywhere you were referencing the current property of that store, you are now referencing the user property.
# UserModel
A "teams" attribute was added to UserModel. If you have extended this file, make sure to copy across the additions to the constructor() and toJSON() functions, as well as require TeamModel and RoleModel in your UserModel, as in the copy in wood.
# Configuration
Configuration values are now loaded with the getConfig() function defined in #lib/Config. This allows for easy configuration value overrides when testing. For more information, check the Configuration section of the docs. Your old ways of loading configuration values will still work, but this style is officially deprecated.
For added security, you'll need to move your transportConfig configuration value from config/email.js to config-api/email.js. This is a new folder that has protections to keep it from being loaded in the UI front-end, making it safe to store API back-end configuration secrets. When loading values from files in this folder, you'll need to use the getConfigApi() function defined in #lib/Config. This function works much like getConfig(), except with protections to ensure it cannot be called from within webpack, protecting these files from being included in your UI front-end.
# 0.16.0
# Modifying app/ui/main.js to take into account the new samples feature
Instead of polluting the default main.js with routes for sample files, those files and their routes have been moved to the samples feature in the wood library, which can be cleanly disabled by removing the feature from your app/config/app.js file. Consequently, those routes can be removed from app/ui/main.js, which can be pared down to simply:
import main from '@wood/ui/main';
main();
You will also want to override the wood feature list in app/config/app.js, if you haven't already, and remove the samples feature, like so:
features: {
// (Keep your existing "app" features here!)
/**
* @type {Array<String>} List of enabled Nodewood features.
*/
wood: [
// 'samples',
'users',
'admin',
'subscriptions',
],
},
# Add eslint-plugin-promise to your eslintrc.js
The eslint-plugin-promise (opens new window) set of rules are designed to prevent you from making common mistakes with promises and async/await code. These mistakes can lead to especially hard-to-debug errors, so following these eslint rules will make a significant difference in your project.
To enable them, edit your .eslintrc.js file in your application root, and make sure the following two sections look like this:
extends: [
// Common
'eslint:recommended',
'plugin:import/errors',
'airbnb-base/legacy',
'plugin:promise/recommended',
// Vue
'plugin:vue/recommended',
'@vue/airbnb',
],
plugins: [
'import',
'promise',
],
# Updating your Webpack Dev Server, and enabling HMR
- Update the following line in the
scriptssection of your your rootpackage.json:
"dev-ui": "cd app && vue-cli-service serve",
If you've created a custom
docker-compose.ymlfile inapp/docker, make sure to add the changes from thedocker-compose.ymlfile inwood/docker, specifically thenetworksandportssection underui, and thelinkssection undernginx.If you've created a custom
ngingx-default.conffile inapp/docker, make sure to add the newlocationsection from thedocker-compose.ymlfile inwood/docker.If you use anything other than
localhostfor your local development environment (for example, if you've created a/etc/hostsentry for your Nodewood development), you'll need to put that URL in yourdevServer.publicconfiguration inapp/vue.config.js. For example, if you go to "https://develop.test/app" in your browser, you'll need to modifyapp/vue.config.jslike so:
const config = require('../wood/vue.config.js');
module.exports = {
...config,
devServer: {
port: 9000,
public: 'develop.test',
stats: 'minimal',
progress: false,
},
};
- Finally, you'll need to destroy and rebuild your Nginx images:
docker-compose -p PROJECT_NAME -f wood/docker/docker-compose.yml rm -sv nginx
docker-compose -p PROJECT_NAME -f wood/docker/docker-compose.yml up --build --force-recreate --no-deps nginx
Where PROJECT_NAME is the basename of the directory your project lives in. For example, if your project is in ~/projects/myproject, your PROJECT_NAME is myproject. If you have a period in your directory (for example myproject.com), the period is omitted completely for your PROJECT_NAME (myprojectcom). If you use a custom docker-compose.yml, change wood to app in the lines above.
# 0.15.0
# Error message: Cannot find module '@tailwindcss/forms'
You will see this error message during upgrade, as we are upgrading to Tailwind 2, which replaces the @tailwindcss/custom-forms plugin with the @tailwindcss/forms plugin. Since part of the Nodewood upgrade process is trying to apply your chosen Tailwind prefix to all Tailwind classes, this step will break.
Thankfully, it's a pretty easy fix:
- Complete the Other Packages step below.
- Run
yarn install. - If you have set a Tailwind prefix, run
nodewood tailwind:prefixand apply your prefix again.
# app/ui/main.js
All the code for this file has been moved to wood/ui/main.js so that Nodewood library updates to this file are automatically applied instead of relying on you to manually update this file. In order to take advantage of this, however, you will need to replace the content of app/ui/main.js with the following:
import main from '@wood/ui/main';
import router from '#ui/router';
main();
router.addRoute({
path: '/',
name: 'home',
component: () => import(/* webpackChunkName: "home" */ '#ui/pages/HomePage'),
});
router.addRoute({
path: '/about',
name: 'about',
component: () => import(/* webpackChunkName: "about" */ '#ui/pages/AboutPage'),
meta: {
routeName: 'About',
},
});
router.addRoute({
path: '/components',
name: 'components',
component: () => import(/* webpackChunkName: "components" */ '#ui/pages/ComponentsPage'),
meta: {
routeName: 'Components',
},
});
You can still import Vue at the beginning of this file and add custom Vue.use directives, etc, before calling main(), and if you have heavily customized this file already, you can leave your customizations in place. However, further updates to main.js may need to be manually applied to your customized file.
# Nginx
Tired of seeing all those OPTIONS logs? This release configures nginx to skip those lines when logging, resulting in cleaner log output. You'll need to rebuild your nginx Docker container to activate this new configuration:
docker-compose -p PROJECT_NAME -f wood/docker/docker-compose.yml build --no-cache nginx
Where PROJECT_NAME is the basename of the directory your project lives in. For example, if your project is in ~/projects/myproject, your PROJECT_NAME is myproject. If you have a period in your directory (for example myproject.com), the period is omitted completely for your PROJECT_NAME (myprojectcom).
# Tailwind 2
Tailwind 2 brings a lot of neat new capabilities, but it may require a little work to get working as expected. To start, you should read the official upgrade guide. (opens new window) Not all of it will be relevant, but it will give you an overview of what's changed.
Nodewood used the @tailwindcss/custom-forms plugin, which has been replaced with the @tailwindcss/forms plugin. This new plugin styles all forms cleanly by default, instead of just all the ones you added form-* classes to. You can safely go and remove all these classes, as they no longer do anything.
Of particular notice is how @apply statement order has changed (opens new window). This may mean that some classes now apply in a different order than you expect, and you'll have to make a small adjustment to that order in order to get your styles working again.
# Other Packages
Many other packages have had their versions updated as well, so you'll want to update the packages in the dependencies section of your app/package.json to the following versions:
"@vue/cli-plugin-babel": "^4.5.11",
"@vue/cli-plugin-eslint": "^4.5.11",
"@vue/cli-plugin-unit-jest": "^4.5.11",
"@vue/cli-service": "^4.5.11",
"@vue/eslint-config-airbnb": "^5.3.0",
"aws-sdk": "^2.839.0",
"core-js": "^3.8.3",
"debug": "^4.3.1",
"enhanced-resolve": "^5.7.0",
"eslint": "^7.19.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jest": "^24.1.3",
"eslint-plugin-vue": "^7.5.0",
"hashids": "^2.2.8",
"massive": "^6.6.5",
"moment": "^2.29.1",
"nodemailer": "^6.4.17",
"nodemailer-mock": "^1.5.3",
"pino": "^6.11.1",
"stripe": "^8.135.0",
"superagent": "^6.1.0",
"supertest": "^6.1.3",
"vue": "2.6.12",
"vue-js-modal": "^2.0.0-rc.6",
"vue-router": "^3.5.1",
"vue-template-compiler": "2.6.12",
"vuex": "^3.6.2"
You'll also want to update the packages in the devDependencies section of your package.json (in the root of your project) to the following versions:
"nodemon": "^2.0.7",
"eslint": "^7.19.0",
"husky": "^5.0.9",
"lint-staged": "^10.5.4"
In addition the following packages have been moved to the wood library or removed, and can be removed from all sections of your app/package.json file:
"jest",
"knex",
"knex-cleaner",
"pino-pretty",
"socket.io",
"v-click-outside",
"webpack",
"webpack-bundle-analyzer",
This will update some of the eslint rules that we uses to keep code tidy. Specifically, anywhere that you use a v-slot directive in your Vue components, you'll need to replace it with the # shorthand. Specifically, v-slot:slotname will need to change to #slotname.
# 0.13.0
This release greatly simplifies the specific inclusion of files in the app and wood folders. Instead of prefixing your includes with @wood-config/file or @app-ui/file, you will instead prefix them with @wood/config/file or @app/ui/file. This may seem superficial, but it means that we only have special cases for the wood and app folders, instead of special cases for every folder under each of them. This greatly simplifies the require logic behind the scenes, and means that you can now specifically require any file in the wood and app folders, not just ones with specifically-defined aliases.
Upgrade steps:
- Update
_moduleAliasesinpackage.json:
"_moduleAliases": {
"@app": "./app",
"@wood": "./wood"
},
- Update
moduleNameMapperinjest.config.js:
moduleNameMapper: {
'^@app(.*)$': '<rootDir>/app/$1',
'^@wood(.*)$': '<rootDir>/wood/$1',
},
- Update
settings.import/resolver.alias-array.mapin.eslintrc.js:
map: [
['#api', [
resolve(__dirname, 'app/api'),
resolve(__dirname, 'wood/api'),
]],
['#config', [
resolve(__dirname, 'app/config'),
resolve(__dirname, 'wood/config'),
]],
['#features', [
resolve(__dirname, 'app/features'),
resolve(__dirname, 'wood/features'),
]],
['#lib', [
resolve(__dirname, 'app/lib'),
resolve(__dirname, 'wood/lib'),
]],
['#ui', [
resolve(__dirname, 'app/ui'),
resolve(__dirname, 'wood/ui'),
]],
['@app', resolve(__dirname, 'app')],
['@wood', resolve(__dirname, 'wood')],
],
- Update
_moduleAliasesinapp/package.json:
"_moduleAliases": {
"@app": ".",
"@wood": "../wood/"
},
- Replace all instances of
@wood-rootwith@wood. - Replace all instances of
@app-rootwith@app. - Replace all instances of
@wood-with@wood/. - Replace all instances of
@app-with@app/.
# 0.12.0
This release is a significant change in the file layout of Nodewood. If you are upgrading from < 0.12.0 and do not perform these changes, your app will not work.
This release moves all files in api/src to api, lib/src to lib, and ui/src to ui to better match how the rest of the framework is structured. These files were originally in separate root folders, but were all moved under the wood folder a while back and should properly have had their src folder removed at that time.
But as they say, the best time to plant a tree is twenty years ago, and the second-best time is now.
Upgrade steps:
- Move all code from your
app/api/srcfolder to theapp/apifolder. - Move all code from your
app/lib/srcfolder to theapp/libfolder. - Move all code from your
app/ui/srcfolder to theapp/uifolder. - Update
.eslintrc.jsto remove the/srcsuffix for all entries. - Update
jest.config.jsto change all instances ofsrc/$1to$1. - Update
package.jsonto remove thesrcsuffix for all entries in_moduleAliases. - Update
package.jsonto changemainto"app/api/api.js". - Update
package.jsonto changescripts.dev-api-onlyto"cd app/api && NODE_ENV=development nodemon --watch . --watch ../config --watch ../features --watch ../../wood -L ./api.js | npx pino-pretty" - Update
app/package.jsonto remove thesrcsuffix for all entries in_moduleAliases. - Update
app/api/api.jsand change thestartServerline toconst { startServer } = require('../../wood/api/api.js');. - Update `app/
- Update any other custom code you have added that refers to
api/src,lib/src, orui/src. - Update any other custom code you have added to the
api,lib, oruifolders that access files from other folders using relative paths (i.e. if you have a sequence of../../../, etc).
If you experience into any issues, please contact support at hello@nodewood.com.