Skip to content

Commit

Permalink
refactor: type-check the code and update types (#1114)
Browse files Browse the repository at this point in the history
- fix a bunch of smaller issues with types (missing `cookieCrossOrigin`
  and `cookieSecure` from `DetectBrowserLanguageInterface` type for example).
- expose types individually rather than through a `NuxtVueI18n` namespace.
  Namespace kept but deprecated.
- add `localeCodes` property on i18n/$i18n object
- drop support for `vue-loader` <15.0.0 which has not been in use for many years now.
- add husky for linting on commit

Even though the diff looks huge, there should be no functional changes apart
from those listed above.
  • Loading branch information
rchl committed Mar 23, 2021
1 parent 7b17b39 commit 2b033ca
Show file tree
Hide file tree
Showing 30 changed files with 985 additions and 552 deletions.
2 changes: 2 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ module.exports = {
rules: {
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
'arrow-parens': 'off',
'import/named': 'off',
'import/namespace': 'off',
'no-console': [
'error', {
allow: ['assert', 'warn', 'error', 'info']
Expand Down
6 changes: 6 additions & 0 deletions docs/content/en/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ Instance of [VueI18n class](http://kazupon.github.io/vue-i18n/api/#vuei18n-class

Default locale as specified in options.

#### localeCodes

- **Type**: `Array<string>`

List of locale codes of registered locales.

#### locales

- **Type**: `Array<string | LocaleObject>`
Expand Down
6 changes: 6 additions & 0 deletions docs/content/es/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ Instance of [VueI18n class](http://kazupon.github.io/vue-i18n/api/#vuei18n-class

Default locale as specified in options.

#### localeCodes

- **Type**: `Array<string>`

List of locale codes of registered locales.

#### locales

- **Type**: `Array<string | LocaleObject>`
Expand Down
18 changes: 14 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"dev:basic:generate": "nuxt generate -c ./test/fixture/basic/nuxt.config.js",
"start:dist": "jiti ./test/utils/http-server-internal.js --port 8080 -v dist",
"coverage": "codecov",
"lint": "eslint --ext .js,.vue,.ts src test types",
"lint": "eslint --ext .js,.vue,.ts src test types && tsc",
"test": "yarn test:types && yarn test:unit && yarn test:e2e-ssr && yarn test:e2e-browser",
"test:e2e-ssr": "jest test/module.test",
"test:e2e-browser": "jest test/browser.test",
Expand All @@ -36,10 +36,16 @@
"docs:dev": "cd ./docs && yarn dev && cd ..",
"docs:build": "cd ./docs && yarn generate && cd .."
},
"husky": {
"hooks": {
"pre-commit": "npm run lint",
"post-merge": "yarn"
}
},
"eslintIgnore": [
"src/templates/options.js",
"test/fixture/typescript/**/*",
"**/*.d.ts"
"types/vue.d.ts"
],
"files": [
"src",
Expand Down Expand Up @@ -75,7 +81,6 @@
"collectCoverageFrom": [
"src/**/*.js",
"!src/templates/*.js",
"!src/plugins/*.js",
"!src/helpers/utils.js",
"!src/helpers/constants.js"
]
Expand All @@ -95,16 +100,19 @@
"@babel/core": "7.13.8",
"@babel/preset-env": "7.13.8",
"@babel/runtime": "7.13.8",
"@nuxt/types": "2.15.2",
"@nuxt/types": "2.15.3",
"@nuxtjs/composition-api": "0.21.0",
"@nuxtjs/eslint-config-typescript": "6.0.0",
"@nuxtjs/module-test-utils": "1.6.3",
"@release-it/conventional-changelog": "2.0.1",
"@types/argparse": "2.0.5",
"@types/cookie": "^0.4.0",
"@types/finalhandler": "1.1.0",
"@types/jest": "26.0.20",
"@types/jest-dev-server": "4.2.0",
"@types/js-cookie": "^2.2.6",
"@types/jsdom": "16.2.6",
"@types/lodash.merge": "^4.6.6",
"@types/request-promise-native": "1.0.17",
"@types/serve-static": "1.13.9",
"argparse": "2.0.1",
Expand All @@ -114,9 +122,11 @@
"core-js": "3.9.1",
"eslint": "7.21.0",
"finalhandler": "1.1.2",
"husky": "4.3.8",
"jest": "26.6.3",
"jest-dev-server": "4.4.0",
"jsdom": "16.4.0",
"lodash.merge": "^4.6.2",
"messageformat": "2.3.0",
"nuxt": "2.15.2",
"playwright-chromium": "1.9.1",
Expand Down
46 changes: 25 additions & 21 deletions src/core/hooks.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { MODULE_NAME, STRATEGIES } from '../helpers/constants'
import { STRATEGIES } from '../helpers/constants'
import { formatMessage } from '../templates/utils-common'

export function createExtendRoutesHook (moduleContainer, options) {
const nuxtOptions = moduleContainer.options
/**
* @this {import('@nuxt/types/config/module').ModuleThis}
*
* @param {import('../../types/internal').ResolvedOptions} options
* @return {import('@nuxt/types/config/router').NuxtOptionsRouter['extendRoutes']}
*/
export function createExtendRoutesHook (options) {
const nuxtOptions = this.options

let includeUprefixedFallback = nuxtOptions.target === 'static'
// Doesn't seem like we can tell whether we are in nuxt generate from the module so we'll
// take advantage of the 'generate:before' hook to store variable.
moduleContainer.nuxt.hook('generate:before', () => { includeUprefixedFallback = true })
this.nuxt.hook('generate:before', () => { includeUprefixedFallback = true })

const pagesDir = nuxtOptions.dir && nuxtOptions.dir.pages ? nuxtOptions.dir.pages : 'pages'
const { trailingSlash } = nuxtOptions.router
Expand All @@ -28,33 +35,30 @@ export function createExtendRoutesHook (moduleContainer, options) {
}
}

export function buildHook (moduleContainer, options) {
/**
* @this {import('@nuxt/types/config/module').ModuleThis}
*
* @param {import('../../types/internal').ResolvedOptions} options
*/
export function buildHook (options) {
if (options.strategy === STRATEGIES.NO_PREFIX && options.differentDomains) {
// eslint-disable-next-line no-console
console.warn('[' + MODULE_NAME + '] The `differentDomains` option and `no_prefix` strategy are not compatible. Change strategy or disable `differentDomains` option.')
console.warn(formatMessage('The `differentDomains` option and `no_prefix` strategy are not compatible. Change strategy or disable `differentDomains` option.'))
}

if ('forwardedHost' in options) {
// eslint-disable-next-line no-console
console.warn('[' + MODULE_NAME + '] The `forwardedHost` option is deprecated. You can safely remove it. See: https://github.com/nuxt-community/i18n-module/pull/630.')
console.warn(formatMessage('The `forwardedHost` option is deprecated. You can safely remove it. See: https://github.com/nuxt-community/i18n-module/pull/630.'))
}

// Add vue-i18n-loader if applicable
if (options.vueI18nLoader) {
moduleContainer.extendBuild(config => {
const vueLoader = config.module.rules.find(el => el.loader.includes('vue-loader'))
if (vueLoader && vueLoader.options && vueLoader.options.loaders) {
// vue-loader under 15.0.0
/* istanbul ignore next */
vueLoader.options.loaders.i18n = require.resolve('@intlify/vue-i18n-loader')
} else {
// vue-loader after 15.0.0
config.module.rules.push({
resourceQuery: /blockType=i18n/,
type: 'javascript/auto',
loader: require.resolve('@intlify/vue-i18n-loader')
})
}
this.extendBuild(config => {
config.module?.rules.push({
resourceQuery: /blockType=i18n/,
type: 'javascript/auto',
loader: require.resolve('@intlify/vue-i18n-loader')
})
})
}
}
43 changes: 29 additions & 14 deletions src/helpers/components.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,43 @@
const { readFileSync } = require('fs')
const parser = require('@babel/parser')
const traverse = require('@babel/traverse').default
import { readFileSync } from 'fs'
import { parse } from '@babel/parser'
import traverse from '@babel/traverse'
// Must not be an explicit dependency to avoid version mismatch issue.
// See https://github.com/nuxt-community/i18n-module/issues/297
const compiler = require('vue-template-compiler')
const { COMPONENT_OPTIONS_KEY, MODULE_NAME } = require('./constants')
import { parseComponent } from 'vue-template-compiler'
import { formatMessage } from '../templates/utils-common'
import { COMPONENT_OPTIONS_KEY } from './constants'

/**
* Extracts nuxtI18n component options for given component file path.
*
* @param {string} path The path to the component file
* @return {Record<string, any>}
* @typedef {Required<Pick<import('../../types/vue').NuxtI18nComponentOptions, 'locales' | 'paths'>>} ComputedPageOptions
*
* @param {import('@nuxt/types/config/router').NuxtRouteConfig['component']} component
* @return {ComputedPageOptions | false}
*/
exports.extractComponentOptions = path => {
let componentOptions = {}
export function extractComponentOptions (component) {
if (typeof (component) !== 'string') {
return false
}

/** @type {ComputedPageOptions | false} */
let componentOptions = {
locales: [],
paths: {}
}

let contents
try {
contents = readFileSync(path).toString()
contents = readFileSync(component).toString()
} catch (error) {
console.warn(`[${MODULE_NAME}] Couldn't read page component file (${error.message})`)
console.warn(formatMessage(`Couldn't read page component file (${error.message})`))
}

if (!contents) {
return componentOptions
}

const Component = compiler.parseComponent(contents)
const Component = parseComponent(contents)

if (!Component.script || Component.script.content.length < 1) {
return componentOptions
Expand All @@ -34,7 +46,7 @@ exports.extractComponentOptions = path => {
const script = Component.script.content

try {
const parsed = parser.parse(script, {
const parsed = parse(script, {
sourceType: 'module',
plugins: [
'nullishCoalescingOperator',
Expand All @@ -50,8 +62,11 @@ exports.extractComponentOptions = path => {

traverse(parsed, {
enter (path) {
// @ts-ignore
if (path.node.type === 'Property') {
// @ts-ignore
if (path.node.key.name === COMPONENT_OPTIONS_KEY) {
// @ts-ignore
const data = script.substring(path.node.start, path.node.end)
componentOptions = Function(`return ({${data}})`)()[COMPONENT_OPTIONS_KEY] // eslint-disable-line
}
Expand All @@ -60,7 +75,7 @@ exports.extractComponentOptions = path => {
})
} catch (error) {
// eslint-disable-next-line no-console
console.warn('[' + MODULE_NAME + `] Error parsing "${COMPONENT_OPTIONS_KEY}" component option in file "${path}".`)
console.warn(formatMessage(`Error parsing "${COMPONENT_OPTIONS_KEY}" component option in file "${component}"`))
}

return componentOptions
Expand Down
41 changes: 20 additions & 21 deletions src/helpers/constants.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,47 @@
const packageJson = require('../../package.json')
/**
* @typedef {import('../../types').Options} Options
*/

// Internals
exports.MODULE_NAME = packageJson.name
exports.ROOT_DIR = 'nuxt-i18n'
exports.LOCALE_CODE_KEY = 'code'
exports.LOCALE_ISO_KEY = 'iso'
exports.LOCALE_DIR_KEY = 'dir'
exports.LOCALE_DOMAIN_KEY = 'domain'
exports.LOCALE_FILE_KEY = 'file'
export const ROOT_DIR = 'nuxt-i18n'

// Options
const STRATEGIES = {
PREFIX: 'prefix',
PREFIX_EXCEPT_DEFAULT: 'prefix_except_default',
PREFIX_AND_DEFAULT: 'prefix_and_default',
NO_PREFIX: 'no_prefix'
const STRATEGY_PREFIX = 'prefix'
const STRATEGY_PREFIX_EXCEPT_DEFAULT = 'prefix_except_default'
const STRATEGY_PREFIX_AND_DEFAULT = 'prefix_and_default'
const STRATEGY_NO_PREFIX = 'no_prefix'
export const STRATEGIES = {
PREFIX: STRATEGY_PREFIX,
PREFIX_EXCEPT_DEFAULT: STRATEGY_PREFIX_EXCEPT_DEFAULT,
PREFIX_AND_DEFAULT: STRATEGY_PREFIX_AND_DEFAULT,
NO_PREFIX: STRATEGY_NO_PREFIX
}

exports.STRATEGIES = STRATEGIES
export const COMPONENT_OPTIONS_KEY = 'nuxtI18n'

exports.COMPONENT_OPTIONS_KEY = 'nuxtI18n'
exports.DEFAULT_OPTIONS = {
/** @type {Options} */
export const DEFAULT_OPTIONS = {
vueI18n: {},
vueI18nLoader: false,
locales: [],
defaultLocale: '',
defaultDirection: 'ltr',
routesNameSeparator: '___',
defaultLocaleRouteNameSuffix: 'default',
strategy: STRATEGIES.PREFIX_EXCEPT_DEFAULT,
strategy: STRATEGY_PREFIX_EXCEPT_DEFAULT,
lazy: false,
langDir: null,
rootRedirect: null,
detectBrowserLanguage: {
useCookie: true,
alwaysRedirect: false,
cookieCrossOrigin: false,
cookieDomain: null,
cookieKey: 'i18n_redirected',
cookieSecure: false,
alwaysRedirect: false,
fallbackLocale: '',
onlyOnNoPrefix: false,
onlyOnRoot: false
onlyOnRoot: false,
useCookie: true
},
differentDomains: false,
seo: false,
Expand All @@ -58,4 +58,3 @@ exports.DEFAULT_OPTIONS = {
beforeLanguageSwitch: () => null,
onLanguageSwitched: () => null
}
exports.NESTED_OPTIONS = ['detectBrowserLanguage', 'vuex']
Loading

0 comments on commit 2b033ca

Please sign in to comment.