Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: vitejs/vite
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: d4f13bd81468961c8c926438e815ab6b1c82735e
Choose a base ref
...
head repository: vitejs/vite
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: e41d78e151328dba81750a2ea56e6cf2c5828e2b
Choose a head ref
  • 18 commits
  • 51 files changed
  • 14 contributors

Commits on Jul 25, 2023

  1. 1
    Copy the full SHA
    a48bf88 View commit details
  2. 1
    Copy the full SHA
    aeef670 View commit details
  3. 1
    Copy the full SHA
    6251a66 View commit details
  4. 1

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    af53a1d View commit details
  5. 1
    Copy the full SHA
    df77991 View commit details

Commits on Jul 27, 2023

  1. 1
    Copy the full SHA
    24c12fe View commit details

Commits on Jul 28, 2023

  1. 1
    Copy the full SHA
    89a3db0 View commit details
  2. 1
    Copy the full SHA
    dd9d4c1 View commit details
  3. fix(importMetaGlob): avoid unnecessary hmr of negative glob (#13646)

    Co-authored-by: fuhao.xu <fuhao.xu@yitu-inc.com>
    sun0day and fuhao.xu authored Jul 28, 2023
    1
    Copy the full SHA
    844451c View commit details

Commits on Jul 29, 2023

  1. 1
    Copy the full SHA
    f8a5ffc View commit details
  2. 1
    Copy the full SHA
    b9a8d65 View commit details
  3. 1
    Copy the full SHA
    66f522c View commit details
  4. fix(importAnalysis): strip url base before passing as safeModulePaths (

    …#13712)
    
    Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com>
    Co-authored-by: sapphi-red <49056869+sapphi-red@users.noreply.github.com>
    3 people authored Jul 29, 2023
    1
    Copy the full SHA
    1ab06a8 View commit details

Commits on Jul 31, 2023

  1. 1
    Copy the full SHA
    3aab14e View commit details
  2. 1
    Copy the full SHA
    65e5c22 View commit details
  3. 1
    Copy the full SHA
    4ca7c13 View commit details
  4. 1
    Copy the full SHA
    488085d View commit details
  5. release: v4.4.8

    patak-dev committed Jul 31, 2023
    1
    Copy the full SHA
    e41d78e View commit details
Showing with 495 additions and 128 deletions.
  1. +5 −2 packages/create-vite/template-react-ts/README.md
  2. +1 −1 packages/plugin-legacy/package.json
  3. +1 −1 packages/plugin-legacy/src/index.ts
  4. +21 −0 packages/vite/CHANGELOG.md
  5. +4 −4 packages/vite/package.json
  6. +2 −2 packages/vite/rollup.config.ts
  7. +4 −2 packages/vite/scripts/postPatchTypes.ts
  8. +15 −0 packages/vite/src/node/__tests__/plugins/esbuild.spec.ts
  9. +2 −1 packages/vite/src/node/config.ts
  10. +6 −1 packages/vite/src/node/logger.ts
  11. +9 −3 packages/vite/src/node/optimizer/index.ts
  12. +1 −1 packages/vite/src/node/optimizer/optimizer.ts
  13. +27 −11 packages/vite/src/node/optimizer/scan.ts
  14. +3 −1 packages/vite/src/node/plugins/asset.ts
  15. +4 −3 packages/vite/src/node/plugins/assetImportMetaUrl.ts
  16. +5 −3 packages/vite/src/node/plugins/css.ts
  17. +14 −0 packages/vite/src/node/plugins/esbuild.ts
  18. +5 −3 packages/vite/src/node/plugins/html.ts
  19. +5 −1 packages/vite/src/node/plugins/importAnalysis.ts
  20. +15 −18 packages/vite/src/node/plugins/importAnalysisBuild.ts
  21. +21 −3 packages/vite/src/node/plugins/importMetaGlob.ts
  22. +1 −2 packages/vite/src/node/plugins/index.ts
  23. +13 −14 packages/vite/src/node/plugins/reporter.ts
  24. +3 −1 packages/vite/src/node/plugins/worker.ts
  25. +1 −1 packages/vite/src/node/server/index.ts
  26. +1 −1 packages/vite/src/node/server/middlewares/indexHtml.ts
  27. +2 −2 packages/vite/src/node/server/middlewares/static.ts
  28. +1 −1 packages/vite/src/node/server/pluginContainer.ts
  29. +1 −1 packages/vite/src/node/ssr/ssrTransform.ts
  30. +23 −1 packages/vite/src/node/utils.ts
  31. +8 −0 playground/css-codesplit/__tests__/css-codesplit.spec.ts
  32. +4 −0 playground/css-codesplit/shared-css-empty-1.js
  33. +4 −0 playground/css-codesplit/shared-css-empty-2.js
  34. +10 −0 playground/css-codesplit/shared-css-main.js
  35. +4 −0 playground/css-codesplit/shared-css-no-js.html
  36. +3 −0 playground/css-codesplit/shared-css-theme.css
  37. +6 −0 playground/css-codesplit/shared-css-with-js.html
  38. +2 −0 playground/css-codesplit/vite.config.js
  39. +1 −1 playground/css-sourcemap/__tests__/css-sourcemap.spec.ts
  40. +1 −1 playground/css-sourcemap/package.json
  41. +1 −1 playground/css-sourcemap/vite.config.js
  42. +104 −0 playground/fs-serve/__tests__/base/fs-serve-base.spec.ts
  43. +5 −6 playground/fs-serve/__tests__/fs-serve.spec.ts
  44. +4 −1 playground/fs-serve/package.json
  45. +50 −16 playground/fs-serve/root/src/index.html
  46. +36 −0 playground/fs-serve/root/vite.config-base.js
  47. +12 −0 playground/glob-import/__tests__/glob-import.spec.ts
  48. +4 −0 playground/glob-import/dir/index.js
  49. +1 −1 playground/js-sourcemap/__tests__/js-sourcemap.spec.ts
  50. +4 −1 playground/preload/__tests__/preload-disabled/preload-disabled.spec.ts
  51. +15 −15 pnpm-lock.yaml
7 changes: 5 additions & 2 deletions packages/create-vite/template-react-ts/README.md
Original file line number Diff line number Diff line change
@@ -14,12 +14,15 @@ If you are developing a production application, we recommend updating the config
- Configure the top-level `parserOptions` property like this:

```js
parserOptions: {
export default {
// other rules...
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: ['./tsconfig.json', './tsconfig.node.json'],
tsconfigRootDir: __dirname,
},
},
}
```

- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
2 changes: 1 addition & 1 deletion packages/plugin-legacy/package.json
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@
"@babel/preset-env": "^7.22.9",
"browserslist": "^4.21.9",
"core-js": "^3.31.1",
"magic-string": "^0.30.1",
"magic-string": "^0.30.2",
"regenerator-runtime": "^0.13.11",
"systemjs": "^6.14.1"
},
2 changes: 1 addition & 1 deletion packages/plugin-legacy/src/index.ts
Original file line number Diff line number Diff line change
@@ -397,7 +397,7 @@ function viteLegacyPlugin(options: Options = {}): Plugin[] {
if (config.build.sourcemap) {
return {
code: ms.toString(),
map: ms.generateMap({ hires: true }),
map: ms.generateMap({ hires: 'boundary' }),
}
}
return {
21 changes: 21 additions & 0 deletions packages/vite/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
## <small>4.4.8 (2023-07-31)</small>

* fix: modulePreload false (#13973) ([488085d](https://github.com/vitejs/vite/commit/488085d)), closes [#13973](https://github.com/vitejs/vite/issues/13973)
* fix: multiple entries with shared css and no JS (#13962) ([89a3db0](https://github.com/vitejs/vite/commit/89a3db0)), closes [#13962](https://github.com/vitejs/vite/issues/13962)
* fix: use file extensions on type imports so they work with `moduleResolution: 'node16'` (#13947) ([aeef670](https://github.com/vitejs/vite/commit/aeef670)), closes [#13947](https://github.com/vitejs/vite/issues/13947)
* fix(css): enhance error message for missing preprocessor dependency (#11485) ([65e5c22](https://github.com/vitejs/vite/commit/65e5c22)), closes [#11485](https://github.com/vitejs/vite/issues/11485)
* fix(esbuild): fix static properties transpile when useDefineForClassFields false (#13992) ([4ca7c13](https://github.com/vitejs/vite/commit/4ca7c13)), closes [#13992](https://github.com/vitejs/vite/issues/13992)
* fix(importAnalysis): strip url base before passing as safeModulePaths (#13712) ([1ab06a8](https://github.com/vitejs/vite/commit/1ab06a8)), closes [#13712](https://github.com/vitejs/vite/issues/13712)
* fix(importMetaGlob): avoid unnecessary hmr of negative glob (#13646) ([844451c](https://github.com/vitejs/vite/commit/844451c)), closes [#13646](https://github.com/vitejs/vite/issues/13646)
* fix(optimizer): avoid double-commit of optimized deps when discovery is disabled (#13865) ([df77991](https://github.com/vitejs/vite/commit/df77991)), closes [#13865](https://github.com/vitejs/vite/issues/13865)
* fix(optimizer): enable experimentalDecorators by default (#13981) ([f8a5ffc](https://github.com/vitejs/vite/commit/f8a5ffc)), closes [#13981](https://github.com/vitejs/vite/issues/13981)
* perf: replace startsWith with === (#13989) ([3aab14e](https://github.com/vitejs/vite/commit/3aab14e)), closes [#13989](https://github.com/vitejs/vite/issues/13989)
* perf: single slash does not need to be replaced (#13980) ([66f522c](https://github.com/vitejs/vite/commit/66f522c)), closes [#13980](https://github.com/vitejs/vite/issues/13980)
* perf: use Intl.DateTimeFormatter instead of toLocaleTimeString (#13951) ([af53a1d](https://github.com/vitejs/vite/commit/af53a1d)), closes [#13951](https://github.com/vitejs/vite/issues/13951)
* perf: use Intl.NumberFormat instead of toLocaleString (#13949) ([a48bf88](https://github.com/vitejs/vite/commit/a48bf88)), closes [#13949](https://github.com/vitejs/vite/issues/13949)
* perf: use magic-string hires boundary for sourcemaps (#13971) ([b9a8d65](https://github.com/vitejs/vite/commit/b9a8d65)), closes [#13971](https://github.com/vitejs/vite/issues/13971)
* chore(reporter): remove unnecessary map (#13972) ([dd9d4c1](https://github.com/vitejs/vite/commit/dd9d4c1)), closes [#13972](https://github.com/vitejs/vite/issues/13972)
* refactor: add new overload to the type of defineConfig (#13958) ([24c12fe](https://github.com/vitejs/vite/commit/24c12fe)), closes [#13958](https://github.com/vitejs/vite/issues/13958)



## <small>4.4.7 (2023-07-24)</small>

* fix: `optimizeDeps.include` not working with paths inside packages (#13922) ([06e4f57](https://github.com/vitejs/vite/commit/06e4f57)), closes [#13922](https://github.com/vitejs/vite/issues/13922)
8 changes: 4 additions & 4 deletions packages/vite/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vite",
"version": "4.4.7",
"version": "4.4.8",
"type": "module",
"license": "MIT",
"author": "Evan You",
@@ -86,8 +86,8 @@
"@rollup/plugin-node-resolve": "15.1.0",
"@rollup/plugin-typescript": "^11.1.2",
"@rollup/pluginutils": "^5.0.2",
"@types/pnpapi": "^0.0.2",
"@types/escape-html": "^1.0.2",
"@types/pnpapi": "^0.0.2",
"acorn": "^8.10.0",
"acorn-walk": "^8.2.0",
"cac": "^6.7.14",
@@ -110,7 +110,7 @@
"json-stable-stringify": "^1.0.2",
"launch-editor-middleware": "^2.6.0",
"lightningcss": "^1.21.5",
"magic-string": "^0.30.1",
"magic-string": "^0.30.2",
"micromatch": "^4.0.5",
"mlly": "^1.4.0",
"mrmime": "^1.0.1",
@@ -138,10 +138,10 @@
"peerDependencies": {
"@types/node": ">= 14",
"less": "*",
"lightningcss": "^1.21.0",
"sass": "*",
"stylus": "*",
"sugarss": "*",
"lightningcss": "^1.21.0",
"terser": "^5.4.0"
},
"peerDependenciesMeta": {
4 changes: 2 additions & 2 deletions packages/vite/rollup.config.ts
Original file line number Diff line number Diff line change
@@ -260,7 +260,7 @@ function shimDepsPlugin(deps: Record<string, ShimOptions>): Plugin {

return {
code: magicString.toString(),
map: magicString.generateMap({ hires: true }),
map: magicString.generateMap({ hires: 'boundary' }),
}
}
}
@@ -308,7 +308,7 @@ const __require = require;

return {
code: s.toString(),
map: s.generateMap({ hires: true }),
map: s.generateMap({ hires: 'boundary' }),
}
},
}
6 changes: 4 additions & 2 deletions packages/vite/scripts/postPatchTypes.ts
Original file line number Diff line number Diff line change
@@ -7,10 +7,12 @@ import { rewriteImports, walkDir } from './util'
const dir = dirname(fileURLToPath(import.meta.url))
const nodeDts = resolve(dir, '../dist/node/index.d.ts')

// rewrite `types/*` import to relative import
// rewrite `types/*` import to relative import with file extension
rewriteImports(nodeDts, (importPath) => {
if (importPath.startsWith('types/')) {
return '../../' + importPath
return (
'../../' + (importPath.endsWith('.js') ? importPath : importPath + '.js')
)
}
})

15 changes: 15 additions & 0 deletions packages/vite/src/node/__tests__/plugins/esbuild.spec.ts
Original file line number Diff line number Diff line change
@@ -383,6 +383,21 @@ describe('transformWithEsbuild', () => {
const actual = await transformClassCode('es2022', {})
expect(actual).toBe(defineForClassFieldsFalseTransformedCode)
})

test('useDefineForClassFields: false and static property should not be transpile to static block', async () => {
const result = await transformWithEsbuild(
`
class foo {
static bar = 'bar'
}
`,
'bar.ts',
{
target: 'esnext',
},
)
expect(result?.code).not.toContain('static {')
})
})
})

3 changes: 2 additions & 1 deletion packages/vite/src/node/config.ts
Original file line number Diff line number Diff line change
@@ -119,6 +119,7 @@ export type UserConfigExport =
*/
export function defineConfig(config: UserConfig): UserConfig
export function defineConfig(config: Promise<UserConfig>): Promise<UserConfig>
export function defineConfig(config: UserConfigFnObject): UserConfigFnObject
export function defineConfig(config: UserConfigExport): UserConfigExport
export function defineConfig(config: UserConfigExport): UserConfigExport {
return config
@@ -343,7 +344,7 @@ export interface InlineConfig extends UserConfig {
export type ResolvedConfig = Readonly<
Omit<
UserConfig,
'plugins' | 'css' | 'assetsInclude' | 'optimizeDeps' | 'worker'
'plugins' | 'css' | 'assetsInclude' | 'optimizeDeps' | 'worker' | 'build'
> & {
configFile: string | undefined
configFileDependencies: string[]
7 changes: 6 additions & 1 deletion packages/vite/src/node/logger.ts
Original file line number Diff line number Diff line change
@@ -59,6 +59,11 @@ export function createLogger(
return options.customLogger
}

const timeFormatter = new Intl.DateTimeFormat(undefined, {
hour: 'numeric',
minute: 'numeric',
second: 'numeric',
})
const loggedErrors = new WeakSet<Error | RollupError>()
const { prefix = '[vite]', allowClearScreen = true } = options
const thresh = LogLevels[level]
@@ -77,7 +82,7 @@ export function createLogger(
: type === 'warn'
? colors.yellow(colors.bold(prefix))
: colors.red(colors.bold(prefix))
return `${colors.dim(new Date().toLocaleTimeString())} ${tag} ${msg}`
return `${colors.dim(timeFormatter.format(new Date()))} ${tag} ${msg}`
} else {
return msg
}
12 changes: 9 additions & 3 deletions packages/vite/src/node/optimizer/index.ts
Original file line number Diff line number Diff line change
@@ -27,7 +27,7 @@ import {
import { transformWithEsbuild } from '../plugins/esbuild'
import { ESBUILD_MODULES_TARGET } from '../constants'
import { esbuildCjsExternalPlugin, esbuildDepPlugin } from './esbuildDepPlugin'
import { scanImports } from './scan'
import { resolveTsconfigRaw, scanImports } from './scan'
import { createOptimizeDepsIncludeResolver, expandGlobIds } from './resolve'
export {
initDepsOptimizer,
@@ -713,8 +713,12 @@ async function prepareEsbuildOptimizerRun(

const optimizeDeps = getDepOptimizationConfig(config, ssr)

const { plugins: pluginsFromConfig = [], ...esbuildOptions } =
optimizeDeps?.esbuildOptions ?? {}
const {
plugins: pluginsFromConfig = [],
tsconfig,
tsconfigRaw,
...esbuildOptions
} = optimizeDeps?.esbuildOptions ?? {}

await Promise.all(
Object.keys(depsInfo).map(async (id) => {
@@ -806,6 +810,8 @@ async function prepareEsbuildOptimizerRun(
metafile: true,
plugins,
charset: 'utf8',
tsconfig,
tsconfigRaw: resolveTsconfigRaw(tsconfig, tsconfigRaw),
...esbuildOptions,
supported: {
'dynamic-import': true,
2 changes: 1 addition & 1 deletion packages/vite/src/node/optimizer/optimizer.ts
Original file line number Diff line number Diff line change
@@ -625,7 +625,7 @@ async function createDepsOptimizer(
// It normally should be over by the time crawling of user code ended
await depsOptimizer.scanProcessing

if (!isBuild && optimizationResult) {
if (!isBuild && optimizationResult && !config.optimizeDeps.noDiscovery) {
const result = await optimizationResult.result
optimizationResult = undefined
currentlyProcessing = false
38 changes: 27 additions & 11 deletions packages/vite/src/node/optimizer/scan.ts
Original file line number Diff line number Diff line change
@@ -3,7 +3,13 @@ import fsp from 'node:fs/promises'
import path from 'node:path'
import { performance } from 'node:perf_hooks'
import glob from 'fast-glob'
import type { BuildContext, Loader, OnLoadResult, Plugin } from 'esbuild'
import type {
BuildContext,
BuildOptions,
Loader,
OnLoadResult,
Plugin,
} from 'esbuild'
import esbuild, { formatMessages, transform } from 'esbuild'
import colors from 'picocolors'
import type { ResolvedConfig } from '..'
@@ -224,16 +230,7 @@ async function prepareEsbuildScanner(
logLevel: 'silent',
plugins: [...plugins, plugin],
tsconfig,
tsconfigRaw:
tsconfig || typeof tsconfigRaw === 'string'
? tsconfigRaw
: {
...tsconfigRaw,
compilerOptions: {
experimentalDecorators: true,
...tsconfigRaw?.compilerOptions,
},
},
tsconfigRaw: resolveTsconfigRaw(tsconfig, tsconfigRaw),
...esbuildOptions,
})
}
@@ -666,3 +663,22 @@ function shouldExternalizeDep(resolvedId: string, rawId: string): boolean {
function isScannable(id: string): boolean {
return JS_TYPES_RE.test(id) || htmlTypesRE.test(id)
}

// esbuild v0.18 only transforms decorators when `experimentalDecorators` is set to `true`.
// To preserve compat with the esbuild breaking change, we set `experimentalDecorators` to
// `true` by default if it's unset.
// TODO: Remove this in Vite 5 and check https://github.com/vitejs/vite/pull/13805#issuecomment-1633612320
export function resolveTsconfigRaw(
tsconfig: string | undefined,
tsconfigRaw: BuildOptions['tsconfigRaw'],
): BuildOptions['tsconfigRaw'] {
return tsconfig || typeof tsconfigRaw === 'string'
? tsconfigRaw
: {
...tsconfigRaw,
compilerOptions: {
experimentalDecorators: true,
...tsconfigRaw?.compilerOptions,
},
}
}
4 changes: 3 additions & 1 deletion packages/vite/src/node/plugins/asset.ts
Original file line number Diff line number Diff line change
@@ -189,7 +189,9 @@ export function assetPlugin(config: ResolvedConfig): Plugin {
if (s) {
return {
code: s.toString(),
map: config.build.sourcemap ? s.generateMap({ hires: true }) : null,
map: config.build.sourcemap
? s.generateMap({ hires: 'boundary' })
: null,
}
} else {
return null
7 changes: 4 additions & 3 deletions packages/vite/src/node/plugins/assetImportMetaUrl.ts
Original file line number Diff line number Diff line change
@@ -122,9 +122,10 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
preferRelative: true,
})
file = await assetResolver(url, id)
file ??= url.startsWith('/')
? slash(path.join(config.publicDir, url))
: slash(path.resolve(path.dirname(id), url))
file ??=
url[0] === '/'
? slash(path.join(config.publicDir, url))
: slash(path.resolve(path.dirname(id), url))
}

// Get final asset URL. If the file does not exist,
8 changes: 5 additions & 3 deletions packages/vite/src/node/plugins/css.ts
Original file line number Diff line number Diff line change
@@ -45,6 +45,7 @@ import {
emptyCssComments,
generateCodeFrame,
getHash,
getPackageManagerCommand,
isDataUrl,
isExternalUrl,
isObject,
@@ -697,7 +698,7 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin {
// resolve public URL from CSS paths, we need to use absolute paths
return {
code: s.toString(),
map: s.generateMap({ hires: true }),
map: s.generateMap({ hires: 'boundary' }),
}
} else {
return { code: s.toString() }
@@ -1694,8 +1695,9 @@ function loadPreprocessor(
return (loadedPreprocessors[lang] = _require(resolved))
} catch (e) {
if (e.code === 'MODULE_NOT_FOUND') {
const installCommand = getPackageManagerCommand('install')
throw new Error(
`Preprocessor dependency "${lang}" not found. Did you install it?`,
`Preprocessor dependency "${lang}" not found. Did you install it? Try \`${installCommand} -D ${lang}\`.`,
)
} else {
const message = new Error(
@@ -2130,7 +2132,7 @@ async function getSource(
ms.appendLeft(0, sep)
ms.appendLeft(0, additionalData)

const map = ms.generateMap({ hires: true })
const map = ms.generateMap({ hires: 'boundary' })
map.file = filename
map.sources = [filename]

14 changes: 14 additions & 0 deletions packages/vite/src/node/plugins/esbuild.ts
Original file line number Diff line number Diff line change
@@ -96,6 +96,7 @@ export async function transformWithEsbuild(
}

let tsconfigRaw = options?.tsconfigRaw
const fallbackSupported: Record<string, boolean> = {}

// if options provide tsconfigRaw in string, it takes highest precedence
if (typeof tsconfigRaw !== 'string') {
@@ -150,6 +151,15 @@ export async function transformWithEsbuild(
compilerOptions.experimentalDecorators = true
}

// Compat with esbuild 0.17 where static properties are transpiled to
// static blocks when `useDefineForClassFields` is false. Its support
// is not great yet, so temporarily disable it for now.
// TODO: Remove this in Vite 5, don't pass hardcoded `esnext` target
// to `transformWithEsbuild` in the esbuild plugin.
if (compilerOptions.useDefineForClassFields !== true) {
fallbackSupported['class-static-blocks'] = false
}

// esbuild uses tsconfig fields when both the normal options and tsconfig was set
// but we want to prioritize the normal options
if (options) {
@@ -172,6 +182,10 @@ export async function transformWithEsbuild(
...options,
loader,
tsconfigRaw,
supported: {
...fallbackSupported,
...options?.supported,
},
}

// Some projects in the ecosystem are calling this function with an ESBuildOptions
Loading