# 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.json
andwood/package.json
, add"@vue/compat": "^3.2.0",
to your dependencies. - In
wood/vue.config.js
, add the following inside themodule.exports
object:
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 inapp
andwood
as 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:
appSidebar
renamed toappMenuEntries
adminSidebar
renamed toadminMenuEntries
dropdown
renamed 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
eslint
to"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
globals
section):
globals: { // Vue 3 compiler macros defineProps: 'readonly', defineEmits: 'readonly', defineExpose: 'readonly', withDefaults: 'readonly', },
- Add the following to the
rules
section:
'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
chainWebpack
entry 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.js
to 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
scripts
section of your your rootpackage.json
:
"dev-ui": "cd app && vue-cli-service serve",
If you've created a custom
docker-compose.yml
file inapp/docker
, make sure to add the changes from thedocker-compose.yml
file inwood/docker
, specifically thenetworks
andports
section underui
, and thelinks
section undernginx
.If you've created a custom
ngingx-default.conf
file inapp/docker
, make sure to add the newlocation
section from thedocker-compose.yml
file inwood/docker
.If you use anything other than
localhost
for your local development environment (for example, if you've created a/etc/hosts
entry for your Nodewood development), you'll need to put that URL in yourdevServer.public
configuration inapp/vue.config.js
. For example, if you go to "https://develop.test/app" in your browser, you'll need to modifyapp/vue.config.js
like 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:prefix
and 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
_moduleAliases
inpackage.json
:
"_moduleAliases": {
"@app": "./app",
"@wood": "./wood"
},
- Update
moduleNameMapper
injest.config.js
:
moduleNameMapper: {
'^@app(.*)$': '<rootDir>/app/$1',
'^@wood(.*)$': '<rootDir>/wood/$1',
},
- Update
settings.import/resolver.alias-array.map
in.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
_moduleAliases
inapp/package.json
:
"_moduleAliases": {
"@app": ".",
"@wood": "../wood/"
},
- Replace all instances of
@wood-root
with@wood
. - Replace all instances of
@app-root
with@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/src
folder to theapp/api
folder. - Move all code from your
app/lib/src
folder to theapp/lib
folder. - Move all code from your
app/ui/src
folder to theapp/ui
folder. - Update
.eslintrc.js
to remove the/src
suffix for all entries. - Update
jest.config.js
to change all instances ofsrc/$1
to$1
. - Update
package.json
to remove thesrc
suffix for all entries in_moduleAliases
. - Update
package.json
to changemain
to"app/api/api.js"
. - Update
package.json
to changescripts.dev-api-only
to"cd app/api && NODE_ENV=development nodemon --watch . --watch ../config --watch ../features --watch ../../wood -L ./api.js | npx pino-pretty"
- Update
app/package.json
to remove thesrc
suffix for all entries in_moduleAliases
. - Update
app/api/api.js
and change thestartServer
line 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
, orui
folders 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.