diff --git a/.eslintrc.json b/.eslintrc.json index a69b5f2cf..97a4645f8 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -24,6 +24,21 @@ ], "func-names": [ 0 + ], + "import/no-extraneous-dependencies": [ + 0 + ], + "import/no-unresolved": [ + 2, + { + "ignore": [ + "dayjs" + ] + } + ], + "import/extensions": [ + 2, + "ignorePackages" ] } } \ No newline at end of file diff --git a/.gitignore b/.gitignore index cf2838a38..2ca2c00de 100644 --- a/.gitignore +++ b/.gitignore @@ -12,5 +12,10 @@ package-lock.json # jest coverage -# dist -dist \ No newline at end of file +# build +locale +plugin +dayjs.min.js + +#dev +demo.js \ No newline at end of file diff --git a/.npmignore b/.npmignore index b318b779b..7b67ff777 100644 --- a/.npmignore +++ b/.npmignore @@ -10,4 +10,9 @@ yarn.lock package-lock.json # jest -coverage \ No newline at end of file +coverage + +# dev +src +test +build \ No newline at end of file diff --git a/README.md b/README.md index e614ec76e..0b74d1a8c 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,13 @@ English | [简体中文](./README.zh-CN.md) src="https://img.shields.io/codecov/c/github/xx45/dayjs/master.svg?style=flat-square" alt="Codecov"> License +
+ + Sauce Test Status +

-> Day.js is a minimalist JavaScript library for modern browsers with a largely Moment.js-compatible API. If you use Moment.js, you already know how to use Day.js. +> Day.js is a minimalist JavaScript library that parse, validate, manipulate, and display dates and times for modern browsers with a largely Moment.js-compatible API. If you use Moment.js, you already know how to use Day.js. ```js dayjs().startOf('month').add(1, 'day').set('year', 2018).format('YYYY-MM-DD HH:mm:ss'); diff --git a/README.zh-CN.md b/README.zh-CN.md index 8fe264c57..a9f18193e 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -16,6 +16,10 @@ src="https://img.shields.io/codecov/c/github/xx45/dayjs/master.svg?style=flat-square" alt="Codecov"> License +
+ + Sauce Test Status +

> Day.js 是一个轻量的 JavaScript 时间日期处理库,和 Moment.js 的 API 设计保持完全一样. 如果你曾经用过 Moment.js, 那么你已经知道如何使用 Day.js diff --git a/build/index.js b/build/index.js new file mode 100644 index 000000000..65b5bc75e --- /dev/null +++ b/build/index.js @@ -0,0 +1,45 @@ +const rollup = require('rollup') +const configFactory = require('./rollup.config') +const fs = require('fs') +const util = require('util') +const path = require('path') + +const { promisify } = util + +const promisifyReadDir = promisify(fs.readdir) + +const formatName = n => n.replace(/\.js/, '').replace('-', '_') + +async function build(option) { + const bundle = await rollup.rollup(option.input) + await bundle.write(option.output) +} + +(async () => { + try { + const locales = await promisifyReadDir(path.join(__dirname, '../src/locale')) + locales.forEach((l) => { + build(configFactory({ + input: `./src/locale/${l}`, + fileName: `./locale/${l}`, + name: `dayjs_locale_${formatName(l)}` + })) + }) + + const plugins = await promisifyReadDir(path.join(__dirname, '../src/plugin')) + plugins.forEach((l) => { + build(configFactory({ + input: `./src/plugin/${l}`, + fileName: `./plugin/${l}`, + name: `dayjs_plugin_${formatName(l)}` + })) + }) + + build(configFactory({ + input: './src/index.js', + fileName: './dayjs.min.js' + })) + } catch (e) { + console.error(e) // eslint-disable-line no-console + } +})() diff --git a/build/rollup.config.js b/build/rollup.config.js new file mode 100644 index 000000000..4d31b990e --- /dev/null +++ b/build/rollup.config.js @@ -0,0 +1,28 @@ +const babel = require('rollup-plugin-babel') +const uglify = require('rollup-plugin-uglify') + +module.exports = (config) => { + const { input, fileName, name } = config + return { + input: { + input, + external: [ + 'dayjs' + ], + plugins: [ + babel({ + exclude: 'node_modules/**' + }), + uglify() + ] + }, + output: { + file: fileName, + format: 'umd', + name: name || 'dayjs', + globals: { + dayjs: 'dayjs' + } + } + } +} diff --git a/karma.sauce.conf.js b/karma.sauce.conf.js index a146e974d..4d05721ce 100644 --- a/karma.sauce.conf.js +++ b/karma.sauce.conf.js @@ -82,7 +82,7 @@ module.exports = function (config) { basePath: '', frameworks: ['jasmine'], files: [ - 'dist/*.js', + 'dayjs.min.js', 'test/*spec.js' ], reporters: ['dots', 'saucelabs'], @@ -90,6 +90,7 @@ module.exports = function (config) { colors: true, logLevel: config.LOG_DEBUG, sauceLabs: { + // build: 'Manual', testName: 'Day.js' }, captureTimeout: 200000, // try fix ios timeout diff --git a/package.json b/package.json index d03402263..79e789d92 100644 --- a/package.json +++ b/package.json @@ -1,21 +1,24 @@ { "name": "dayjs", "version": "0.0.0-development", - "description": "2KB immutable date library alternative to Moment.js with the same modern API ", - "main": "dist/dayjs.min.js", + "description": "2KB immutable date time library alternative to Moment.js with the same modern API ", + "main": "dayjs.min.js", "types": "index.d.ts", "scripts": { "test": "jest", - "lint": "./node_modules/.bin/eslint src/* test/*", - "build": "BABEL_ENV=build rollup -c", + "lint": "./node_modules/.bin/eslint src/* test/* build/*", + "build": "BABEL_ENV=build node build", "sauce": "npx karma start karma.sauce.conf.js", "test:sauce": "npm run sauce -- 0 && npm run sauce -- 1 && npm run sauce -- 2 && npm run sauce -- 3", - "gzip-size": "gzip-size ./dist/*.min.js" + "gzip-size": "gzip-size dayjs.min.js" }, "pre-commit": [ "lint" ], "jest": { + "roots": [ + "test" + ], "testRegex": "test/(.*?/)?.*test.js$", "coverageDirectory": "./coverage/", "collectCoverage": true, @@ -23,6 +26,14 @@ "src/**/*" ] }, + "release": { + "prepare": [ + { + "path": "@semantic-release/changelog" + }, + "@semantic-release/git" + ] + }, "keywords": [ "dayjs", "date", diff --git a/rollup.config.js b/rollup.config.js deleted file mode 100644 index 6a1fc4d54..000000000 --- a/rollup.config.js +++ /dev/null @@ -1,18 +0,0 @@ -import babel from 'rollup-plugin-babel' -import uglify from 'rollup-plugin-uglify' -import packageInfo from './package.json'; - -export default { - input: 'src/index.js', - output: { - file: `dist/${packageInfo.name}.min.js`, - format: 'umd', - name: 'dayjs' - }, - plugins: [ - babel({ - exclude: 'node_modules/**' - }), - uglify() - ] -}; \ No newline at end of file diff --git a/src/constant.js b/src/constant.js index d92932022..fc6c079bc 100644 --- a/src/constant.js +++ b/src/constant.js @@ -21,12 +21,15 @@ export const Q = 'quarter' export const Y = 'year' export const DATE = 'date' -export const WEEKDAYS = 'Sunday.Monday.Tuesday.Wednesday.Thursday.Friday.Saturday'.split('.') -export const MONTHS = 'January.February.March.April.May.June.July.August.September.October.November.December'.split('.') - export const FORMAT_DEFAULT = 'YYYY-MM-DDTHH:mm:ssZ' // regex export const REGEX_PARSE = /^(\d{4})-?(\d{1,2})-?(\d{1,2})(.*?(\d{1,2}):(\d{1,2}):(\d{1,2}))?.?(\d{1,3})?$/ export const REGEX_FORMAT = /\[.*?\]|Y{2,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g +export const en = { + name: 'en', + weekdays: 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), + months: 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_') +} + diff --git a/src/index.js b/src/index.js index a4cfc6d84..37dad9eb9 100644 --- a/src/index.js +++ b/src/index.js @@ -1,10 +1,9 @@ import * as C from './constant' -import * as U from './utils' -import en from './locale/en' +import U from './utils' let L = 'en' // global locale const Ls = {} // global loaded locale -Ls[L] = en +Ls[L] = C.en const isDayjs = d => d instanceof Dayjs // eslint-disable-line no-use-before-define diff --git a/src/locale/en.js b/src/locale/en.js deleted file mode 100644 index 9128a9126..000000000 --- a/src/locale/en.js +++ /dev/null @@ -1,7 +0,0 @@ -import * as C from '../constant' - -export default { // may be we just need these two at the present - name: 'en', - weekdays: C.WEEKDAYS, - months: C.MONTHS -} diff --git a/src/locale/es.js b/src/locale/es.js index 7470bffcb..6fc201439 100644 --- a/src/locale/es.js +++ b/src/locale/es.js @@ -1,5 +1,12 @@ -export default { +import dayjs from 'dayjs' + +const locale = { name: 'es', - weekdays: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'], - months: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'] + weekdays: 'Domingo_Lunes_Martes_Miércoles_Jueves_Viernes_Sábado'.split('_'), + months: 'Enero_Febrero_Marzo_Abril_Mayo_Junio_Julio_Agosto_Septiembre_Octubre_Noviembre_Diciembre'.split('_'), + ordinal: n => `${n}º` } + +dayjs.locale(locale, null, true) + +export default locale diff --git a/src/locale/zh-cn.js b/src/locale/zh-cn.js new file mode 100644 index 000000000..6480ebd23 --- /dev/null +++ b/src/locale/zh-cn.js @@ -0,0 +1,12 @@ +import dayjs from 'dayjs' + +const locale = { + name: 'zh-cn', + weekdays: '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'), + months: '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'), + ordinal: n => n +} + +dayjs.locale(locale, null, true) + +export default locale diff --git a/src/plugin/advancedFormat.js b/src/plugin/advancedFormat.js index 10200325b..363fe52de 100644 --- a/src/plugin/advancedFormat.js +++ b/src/plugin/advancedFormat.js @@ -29,3 +29,4 @@ export default (o, c, d) => { // locale needed later return oldFormat.bind(this)(result, locale) } } + diff --git a/src/utils.js b/src/utils.js index 4d727d2f6..de18ac760 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,17 +1,17 @@ -export const padStart = (string, length, pad) => { +const padStart = (string, length, pad) => { const s = String(string) if (!s || s.length >= length) return string return `${Array((length + 1) - s.length).join(pad)}${string}` } -export const padZoneStr = (negMinuts) => { +const padZoneStr = (negMinuts) => { const minutes = Math.abs(negMinuts) const hourOffset = Math.floor(minutes / 60) const minuteOffset = minutes % 60 return `${negMinuts <= 0 ? '+' : '-'}${padStart(hourOffset, 2, '0')}:${padStart(minuteOffset, 2, '0')}` } -export const monthDiff = (a, b) => { +const monthDiff = (a, b) => { // function from moment.js in order to keep the same result const wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()) const anchor = a.clone().add(wholeMonthDiff, 'months') @@ -27,8 +27,17 @@ export const monthDiff = (a, b) => { return Number(-(wholeMonthDiff + adjust)) } -export const absFloor = n => (n < 0 ? Math.ceil(n) || 0 : Math.floor(n)) +const absFloor = n => (n < 0 ? Math.ceil(n) || 0 : Math.floor(n)) -export const prettyUnit = u => (u && String(u).toLowerCase().replace(/s$/, '')) +const prettyUnit = u => (u && String(u).toLowerCase().replace(/s$/, '')) -export const isUndefined = s => s === undefined +const isUndefined = s => s === undefined + +export default { + padStart, + padZoneStr, + monthDiff, + absFloor, + prettyUnit, + isUndefined +} diff --git a/test/__mocks__/dayjs.js b/test/__mocks__/dayjs.js new file mode 100644 index 000000000..b696adbea --- /dev/null +++ b/test/__mocks__/dayjs.js @@ -0,0 +1,3 @@ +const dayjs = require('../../src') + +module.exports = dayjs diff --git a/test/locale.test.js b/test/locale.test.js index e90185b9c..88d34e7cf 100644 --- a/test/locale.test.js +++ b/test/locale.test.js @@ -1,7 +1,6 @@ import MockDate from 'mockdate' import dayjs from '../src' import es from '../src/locale/es' -import en from '../src/locale/en' beforeEach(() => { MockDate.set(new Date()) @@ -39,7 +38,7 @@ it('set locale for this line only', () => { }) it('set global locale', () => { - dayjs.locale(en) + dayjs.locale('en') expect(dayjs('2018-4-28').format(format)) .toBe('Saturday 28, April') dayjs.locale(es) diff --git a/test/locale/keys.test.js b/test/locale/keys.test.js new file mode 100644 index 000000000..e6e4d6fdf --- /dev/null +++ b/test/locale/keys.test.js @@ -0,0 +1,29 @@ +import fs from 'fs' +import path from 'path' +import dayjs from '../../src' + +const localeDir = '../../src/locale' +const L = [] + +// load all locales from locale dir +fs.readdirSync(path.join(__dirname, localeDir)) + .forEach((file) => { + // eslint-disable-next-line + L.push(require(path.join(__dirname, localeDir, file)).default) + }) + +it('Locale keys', () => { + L.forEach((l) => { + const { + name, ordinal, weekdays, months + } = l + expect(name).toEqual(expect.any(String)) + expect(weekdays).toEqual(expect.any(Array)) + expect(months).toEqual(expect.any(Array)) + if (ordinal) { + // function pass date return string or number or null + expect(ordinal(1)).toEqual(expect.anything()) + } + expect(dayjs().locale(name).$locale().name).toBe(name) + }) +}) diff --git a/test/plugin/advancedFormat.test.js b/test/plugin/advancedFormat.test.js index 6b1f5432e..f93676d2f 100644 --- a/test/plugin/advancedFormat.test.js +++ b/test/plugin/advancedFormat.test.js @@ -41,6 +41,8 @@ it('Format Day of Month Do 1 - 31', () => { it('Format Hour k kk 24-hour 1 - 24', () => { expect(dayjs().format('k')).toBe(moment().format('k')) expect(dayjs().format('kk')).toBe(moment().format('kk')) - const d = '2018-05-02 00:00:00.000' + let d = '2018-05-02 00:00:00.000' + expect(dayjs(d).format('k')).toBe(moment(d).format('k')) + d = '2018-05-02 01:00:00.000' expect(dayjs(d).format('k')).toBe(moment(d).format('k')) }) diff --git a/test/utils.test.js b/test/utils.test.js index 5ab21fddc..e70623e79 100644 --- a/test/utils.test.js +++ b/test/utils.test.js @@ -1,4 +1,4 @@ -import * as Utils from '../src/utils' +import Utils from '../src/utils' const { prettyUnit,