diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index bffc6a8a27..e4e18fc96e 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -104,7 +104,7 @@ jobs: strategy: max-parallel: 5 matrix: - python-version: [3.6] + python-version: ['3.10'] steps: - uses: actions/checkout@v4 diff --git a/AUTHORS.md b/AUTHORS.md index c35fcbba7d..d8332e530b 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -103,3 +103,4 @@ If you have contributed to Kolibri, feel free to add your name and Github accoun | Alex VĂ©lez | AlexVelezLl | | Mazen Oweiss | moweiss | | Eshaan Aggarwal | EshaanAgg | +| Nikhil Sharma | ThEditor | diff --git a/kolibri/__init__.py b/kolibri/__init__.py index ce4b84a853..4204b9e824 100755 --- a/kolibri/__init__.py +++ b/kolibri/__init__.py @@ -11,7 +11,7 @@ #: This may not be the exact version as it's subject to modification with #: get_version() - use ``kolibri.__version__`` for the exact version string. -VERSION = (0, 16, 0) +VERSION = (0, 16, 1) __author__ = "Learning Equality" __email__ = "info@learningequality.org" diff --git a/kolibri/core/assets/src/core-app/apiSpec.js b/kolibri/core/assets/src/core-app/apiSpec.js index 207e8e3d9e..386e40db87 100644 --- a/kolibri/core/assets/src/core-app/apiSpec.js +++ b/kolibri/core/assets/src/core-app/apiSpec.js @@ -96,7 +96,6 @@ import LearnOnlyDeviceNotice from '../views/LearnOnlyDeviceNotice'; import themeConfig from '../styles/themeConfig'; import sortLanguages from '../utils/sortLanguages'; import * as sync from '../views/sync/syncComponentSet'; -import PageRoot from '../views/PageRoot'; import NotificationsRoot from '../views/NotificationsRoot'; import useMinimumKolibriVersion from '../composables/useMinimumKolibriVersion'; import useUserSyncStatus from '../composables/useUserSyncStatus'; @@ -203,7 +202,6 @@ export default { PrivacyLinkAndModal, LearnOnlyDeviceNotice, SuggestedTime, - PageRoot, MasteryModel, NotificationsRoot, KolibriLoadingSnippet, diff --git a/kolibri/core/assets/src/kolibri_app.js b/kolibri/core/assets/src/kolibri_app.js index b06390bc80..44f60b9bd9 100644 --- a/kolibri/core/assets/src/kolibri_app.js +++ b/kolibri/core/assets/src/kolibri_app.js @@ -27,7 +27,12 @@ export default class KolibriApp extends KolibriModule { * @return {Object} A component definition for the root component of this single page app. */ get RootVue() { - return {}; + // By default return the component that just renders router-view, + // which will render the component for the current route. + return { + functional: true, + render: createElement => createElement('router-view'), + }; } /* * @return {Store} A convenience getter to return the vuex store. diff --git a/kolibri/core/assets/src/logging.js b/kolibri/core/assets/src/logging.js index 0656ee5aed..801a1bdc4f 100644 --- a/kolibri/core/assets/src/logging.js +++ b/kolibri/core/assets/src/logging.js @@ -5,23 +5,31 @@ import loglevel from 'loglevel'; -const console = global.console; - class Logger { constructor(loggerName) { this.loggerName = loggerName; this.logger = loglevel.getLogger(loggerName); + this.setMessagePrefix(); Object.keys(loglevel.levels).forEach(methodName => { const name = methodName.toLowerCase(); const logFunction = this.logger[name]; if (logFunction) { - this[name] = logFunction.bind(console, this.messagePrefix(name)); + this[name] = param => { + return this.logger[name](param); + }; } }); } - messagePrefix(type) { - return `[${type.toUpperCase()}: ${this.loggerName}]`; + setMessagePrefix() { + var originalFactory = this.logger.methodFactory; + this.logger.methodFactory = function(methodName, logLevel, loggerName) { + var rawMethod = originalFactory(methodName, logLevel, loggerName); + return function(message) { + rawMethod(`[${methodName.toUpperCase()}: ${loggerName}] ` + message); + }; + }; + this.logger.setLevel(this.logger.getLevel()); } setLevel(level, persist) { diff --git a/kolibri/core/assets/src/state/modules/core/actions.js b/kolibri/core/assets/src/state/modules/core/actions.js index a9bad1fcab..3eaf813ae4 100644 --- a/kolibri/core/assets/src/state/modules/core/actions.js +++ b/kolibri/core/assets/src/state/modules/core/actions.js @@ -92,22 +92,6 @@ export function handleApiError(store, { error, reloadOnReconnect = false } = {}) throw error; } -/** - * Used to prevent inadvertent actions if a user double-clicks to navigate - * - * Something of a hack. A better strategy would be to create a new - * `setLoading` action which handles both `state.core.loading` and - * `state.core.blockDoubleClicks` with a single function. - */ -export function blockDoubleClicks(store) { - if (!store.state.blockDoubleClicks) { - store.commit('CORE_BLOCK_CLICKS', true); - setTimeout(() => { - store.commit('CORE_BLOCK_CLICKS', false); - }, 500); - } -} - export function setSession(store, { session, clientNow }) { const serverTime = session.server_time; if (clientNow) { diff --git a/kolibri/core/assets/src/state/modules/core/index.js b/kolibri/core/assets/src/state/modules/core/index.js index 59076b4ed2..f0e72c83ce 100644 --- a/kolibri/core/assets/src/state/modules/core/index.js +++ b/kolibri/core/assets/src/state/modules/core/index.js @@ -10,7 +10,6 @@ export default { state() { return { error: '', - blockDoubleClicks: false, loading: true, pageSessionId: 0, totalProgress: null, diff --git a/kolibri/core/assets/src/state/modules/core/mutations.js b/kolibri/core/assets/src/state/modules/core/mutations.js index ffeeb04c65..a02d320372 100644 --- a/kolibri/core/assets/src/state/modules/core/mutations.js +++ b/kolibri/core/assets/src/state/modules/core/mutations.js @@ -15,9 +15,6 @@ export default { CORE_SET_ERROR(state, error) { state.error = error; }, - CORE_BLOCK_CLICKS(state, blocked) { - state.blockDoubleClicks = blocked; - }, SET_TOTAL_PROGRESS(state, progress) { state.totalProgress = progress; }, diff --git a/kolibri/core/assets/src/views/PageRoot.vue b/kolibri/core/assets/src/views/PageRoot.vue deleted file mode 100644 index 7b89f94169..0000000000 --- a/kolibri/core/assets/src/views/PageRoot.vue +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/kolibri/core/assets/src/views/SyncStatusDisplay.vue b/kolibri/core/assets/src/views/SyncStatusDisplay.vue index 31aa08fa84..364817fcf9 100644 --- a/kolibri/core/assets/src/views/SyncStatusDisplay.vue +++ b/kolibri/core/assets/src/views/SyncStatusDisplay.vue @@ -1,6 +1,6 @@ - + @@ -123,8 +123,6 @@ import throttle from 'lodash/throttle'; import debounce from 'lodash/debounce'; import logger from 'kolibri.lib.logging'; - import { RecycleList } from 'vue-virtual-scroller'; - import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'; // polyfill necessary for recycle list import 'intersection-observer'; import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings'; @@ -132,6 +130,7 @@ import CoreFullscreen from 'kolibri.coreVue.components.CoreFullscreen'; import '../utils/domPolyfills'; import { EventBus } from '../utils/event_utils'; + import RecyclableScroller from './RecyclableScroller'; import PdfPage from './PdfPage'; import SideBar from './SideBar'; @@ -146,8 +145,8 @@ components: { SideBar, PdfPage, - RecycleList, CoreFullscreen, + RecyclableScroller, }, mixins: [commonCoreStrings], setup() { @@ -296,42 +295,38 @@ }; this.eventBus = new EventBus(); this.prepComponentData = loadingPdf.promise - .then(pdfDocument => { + .then(async pdfDocument => { // Get initial info from the loaded pdf document this.pdfDocument = pdfDocument; this.totalPages = this.pdfDocument.numPages; + // Is either the first page or the saved last page visited + const firstPageToRender = parseInt(this.getSavedPosition() * this.totalPages); + + const firstPage = await this.getPage(firstPageToRender + 1); + const viewPort = firstPage.getViewport({ scale: 1 }); + this.firstPageHeight = viewPort.height; + this.firstPageWidth = viewPort.width; + this.scale = this.$el.clientWidth / (this.firstPageWidth * this.screenSizeMultiplier); + // init pdfPages array + // ensuring that firstPageToRender is resolved so that we do not refetch the page for (let i = 0; i < this.totalPages; i++) { this.pdfPages.push({ - page: null, - resolved: false, + page: i == firstPageToRender ? firstPage : null, + resolved: i == firstPageToRender, + size: () => { + return this.firstPageHeight * this.scale + MARGIN; + }, index: i, }); } - // Is either the first page or the saved last page visited - const firstPageToRender = parseInt(this.getSavedPosition() * this.totalPages); - return this.getPage(firstPageToRender + 1).then(firstPage => { - const viewPort = firstPage.getViewport({ scale: 1 }); - this.firstPageHeight = viewPort.height; - this.firstPageWidth = viewPort.width; - this.scale = this.$el.clientWidth / (this.firstPageWidth * this.screenSizeMultiplier); - - // Set the firstPageToRender into the pdfPages object so that we do not refetch the page - // from PDFJS when we do our initial render - // splice so changes are detected - this.pdfPages.splice(firstPageToRender, 1, { - ...this.pdfPages[firstPageToRender], - page: firstPage, - resolved: true, - }); - pdfDocument.getOutline().then(outline => { - this.outline = outline; - this.showSideBar = outline && outline.length > 0 && this.windowIsLarge; // Remove if other tabs are already implemented - // Reduce the scale slightly if we are showing the sidebar - // at first load. - this.scale = this.showSideBar ? 0.75 * this.scale : this.scale; - }); - }); + + const outline = await pdfDocument.getOutline(); + this.outline = outline; + this.showSideBar = outline && outline.length > 0 && this.windowIsLarge; // Remove if other tabs are already implemented + // Reduce the scale slightly if we are showing the sidebar + // at first load. + this.scale = this.showSideBar ? 0.75 * this.scale : this.scale; }) .catch(error => { this.reportLoadingError(error); @@ -379,11 +374,16 @@ if (pageNum > 0 && pageNum <= this.totalPages && !this.pdfPages[pageNum - 1].resolved) { const pageIndex = pageNum - 1; this.getPage(pageNum).then(pdfPage => { + const { height } = pdfPage.getViewport({ scale: 1 }); + // splice so changes are detected this.pdfPages.splice(pageIndex, 1, { ...this.pdfPages[pageIndex], page: pdfPage, resolved: true, + size: () => { + return height * this.scale + MARGIN; + }, }); }); } @@ -703,8 +703,8 @@ margin: 0 auto; } // enable horizontal scrolling - /deep/ .recycle-list { - .item-wrapper { + /deep/ .vue-recycle-scroller { + .vue-recycle-scroller-item-wrapper { overflow-x: auto; } } @@ -748,4 +748,19 @@ height: 100%; } + /deep/ .resize-observer { + position: absolute; + top: 0; + left: 0; + z-index: -1; + display: block; + width: 100%; + height: 100%; + overflow: hidden; + pointer-events: none; + background-color: transparent; + border: 0; + opacity: 0; + } + diff --git a/kolibri/plugins/pdf_viewer/assets/src/views/RecyclableScroller.vue b/kolibri/plugins/pdf_viewer/assets/src/views/RecyclableScroller.vue new file mode 100644 index 0000000000..308e89a50a --- /dev/null +++ b/kolibri/plugins/pdf_viewer/assets/src/views/RecyclableScroller.vue @@ -0,0 +1,887 @@ + + + + + + + + + + diff --git a/kolibri/plugins/pdf_viewer/package.json b/kolibri/plugins/pdf_viewer/package.json index 14b2e9dc31..764765a6c1 100644 --- a/kolibri/plugins/pdf_viewer/package.json +++ b/kolibri/plugins/pdf_viewer/package.json @@ -6,7 +6,7 @@ "dependencies": { "intersection-observer": "0.12.2", "pdfjs-dist": "^2.16.105", - "vue-virtual-scroller": "0.12.0", + "vue-virtual-scroller": "1.1.2", "web-streams-polyfill": "^4.0.0" } } diff --git a/kolibri/plugins/policies/assets/src/app.js b/kolibri/plugins/policies/assets/src/app.js index b48824c7f2..08912ebb37 100644 --- a/kolibri/plugins/policies/assets/src/app.js +++ b/kolibri/plugins/policies/assets/src/app.js @@ -1,4 +1,3 @@ -import PageRoot from 'kolibri.coreVue.components.PageRoot'; import routes from './routes'; import pluginModule from './modules/pluginModule'; import KolibriApp from 'kolibri_app'; @@ -7,9 +6,6 @@ class PoliciesModule extends KolibriApp { get routes() { return routes; } - get RootVue() { - return PageRoot; - } get pluginModule() { return pluginModule; } diff --git a/kolibri/plugins/setup_wizard/assets/src/views/onboarding-forms/DefaultLanguageForm.vue b/kolibri/plugins/setup_wizard/assets/src/views/onboarding-forms/DefaultLanguageForm.vue index ef1854ca6c..24cda2f506 100644 --- a/kolibri/plugins/setup_wizard/assets/src/views/onboarding-forms/DefaultLanguageForm.vue +++ b/kolibri/plugins/setup_wizard/assets/src/views/onboarding-forms/DefaultLanguageForm.vue @@ -1,10 +1,12 @@ @@ -21,11 +23,41 @@ OnboardingStepBase, LanguageSwitcherList, }, + data() { + return { + parentBreakpoint: 4, + }; + }, + mounted() { + this.updateWidth(); + window.addEventListener('resize', this.updateWidth); + }, + beforeDestroy() { + window.removeEventListener('resize', this.updateWidth); + }, inject: ['wizardService'], methods: { handleSubmit() { this.wizardService.send('CONTINUE'); }, + updateWidth() { + const element = this.$refs.container.$el; + const width = element.offsetWidth; + let num = 4; + + if (width < 440) { + num = 0; + } else if (width < 520) { + num = 1; + } else if (width < 600) { + num = 2; + } else if (width < 660) { + num = 3; + } else { + num = 4; + } + this.parentBreakpoint = num; + }, }, $trs: { languageFormHeader: { diff --git a/kolibri/plugins/user_auth/assets/src/views/SignInPage/index.vue b/kolibri/plugins/user_auth/assets/src/views/SignInPage/index.vue index 0c3d19daf0..6c04e1511f 100644 --- a/kolibri/plugins/user_auth/assets/src/views/SignInPage/index.vue +++ b/kolibri/plugins/user_auth/assets/src/views/SignInPage/index.vue @@ -339,6 +339,7 @@ // and forth this.usernameBlurred = false; this.passwordBlurred = false; + this.usernameSubmittedWithoutPassword = false; this.loginError = null; }, // Sets the selected list user and/or logs them in diff --git a/kolibri/plugins/user_auth/assets/src/views/UserAuthLayout.vue b/kolibri/plugins/user_auth/assets/src/views/UserAuthLayout.vue index fbdb18373c..bac2199e5e 100644 --- a/kolibri/plugins/user_auth/assets/src/views/UserAuthLayout.vue +++ b/kolibri/plugins/user_auth/assets/src/views/UserAuthLayout.vue @@ -6,8 +6,6 @@ :style="mainWrapperStyles" > -
-
state.core.error, loading: state => state.core.loading, - blockDoubleClicks: state => state.core.blockDoubleClicks, }), isAuthorized() { return !( @@ -168,15 +165,6 @@ overflow-x: auto; } - .click-mask { - position: fixed; - top: 0; - left: 0; - z-index: 24; - width: 100%; - height: 100%; - } - .debug { font-family: monospace; font-size: large; diff --git a/kolibri/plugins/user_profile/assets/src/app.js b/kolibri/plugins/user_profile/assets/src/app.js index 0fce9ef7fb..522dd1aa59 100644 --- a/kolibri/plugins/user_profile/assets/src/app.js +++ b/kolibri/plugins/user_profile/assets/src/app.js @@ -1,5 +1,4 @@ import router from 'kolibri.coreVue.router'; -import PageRoot from 'kolibri.coreVue.components.PageRoot'; import routes from './routes'; import pluginModule from './modules/pluginModule'; import KolibriApp from 'kolibri_app'; @@ -8,9 +7,6 @@ class UserProfileModule extends KolibriApp { get routes() { return routes; } - get RootVue() { - return PageRoot; - } get pluginModule() { return pluginModule; } diff --git a/kolibri/utils/conf.py b/kolibri/utils/conf.py index 1b86834c21..e6eb04500c 100644 --- a/kolibri/utils/conf.py +++ b/kolibri/utils/conf.py @@ -25,6 +25,7 @@ #: Absolute path of the main user data directory. #: Will be created automatically if it doesn't exist. KOLIBRI_HOME = os.path.abspath(os.path.expanduser(os.environ["KOLIBRI_HOME"])) +NO_FILE_BASED_LOGGING = os.environ.get("KOLIBRI_NO_FILE_BASED_LOGGING", False) # Creating KOLIBRI_HOME atm. has to happen here as for instance utils.cli is not # called through py.test. This file is the first basic entry point of @@ -39,7 +40,7 @@ # Create a folder named logs inside KOLIBRI_HOME to store all the log files. LOG_ROOT = os.path.join(KOLIBRI_HOME, "logs") -if not os.path.exists(LOG_ROOT): +if not os.path.exists(LOG_ROOT) and not NO_FILE_BASED_LOGGING: os.mkdir(LOG_ROOT) diff --git a/kolibri/utils/logger.py b/kolibri/utils/logger.py index fcfa1688a5..d8c0ba68c9 100644 --- a/kolibri/utils/logger.py +++ b/kolibri/utils/logger.py @@ -205,6 +205,43 @@ def get_default_logging_config(LOG_ROOT, debug=False, debug_database=False): DEFAULT_LEVEL = "INFO" if not debug else "DEBUG" DATABASE_LEVEL = "INFO" if not debug_database else "DEBUG" + handlers = { + "console-error": { + "level": "ERROR", + "class": "kolibri.utils.logger.EncodingStreamHandler", + "formatter": "color", + "stream": "ext://sys.stderr", + }, + "console": { + "level": DEFAULT_LEVEL, + "filters": ["no_exceptions"], + "class": "kolibri.utils.logger.EncodingStreamHandler", + "formatter": "color", + "stream": "ext://sys.stdout", + }, + } + + if not NO_FILE_BASED_LOGGING: + handlers["file"] = { + "level": "INFO", + "filters": [], + "class": "kolibri.utils.logger.KolibriTimedRotatingFileHandler", + "filename": os.path.join(LOG_ROOT, "kolibri.txt"), + "formatter": "simple_date", + "when": "midnight", + "backupCount": 30, + "encoding": "utf-8", + } + + handlers["file_debug"] = { + "level": "DEBUG", + "filters": ["require_debug_true"], + "class": "logging.FileHandler", + "filename": os.path.join(LOG_ROOT, "debug.txt"), + "formatter": "simple_date", + "encoding": "utf-8", + } + return { "version": 1, "disable_existing_loggers": False, @@ -223,39 +260,7 @@ def get_default_logging_config(LOG_ROOT, debug=False, debug_database=False): "log_colors": LOG_COLORS, }, }, - "handlers": { - "console-error": { - "level": "ERROR", - "class": "kolibri.utils.logger.EncodingStreamHandler", - "formatter": "color", - "stream": "ext://sys.stderr", - }, - "console": { - "level": DEFAULT_LEVEL, - "filters": ["no_exceptions"], - "class": "kolibri.utils.logger.EncodingStreamHandler", - "formatter": "color", - "stream": "ext://sys.stdout", - }, - "file": { - "level": "INFO", - "filters": [], - "class": "kolibri.utils.logger.KolibriTimedRotatingFileHandler", - "filename": os.path.join(LOG_ROOT, "kolibri.txt"), - "formatter": "simple_date", - "when": "midnight", - "backupCount": 30, - "encoding": "utf-8", - }, - "file_debug": { - "level": "DEBUG", - "filters": ["require_debug_true"], - "class": "logging.FileHandler", - "filename": os.path.join(LOG_ROOT, "debug.txt"), - "formatter": "simple_date", - "encoding": "utf-8", - }, - }, + "handlers": handlers, "loggers": { "": { "handlers": DEFAULT_HANDLERS, diff --git a/packages/browserslist-config-kolibri/package.json b/packages/browserslist-config-kolibri/package.json index 284b10b055..cdeeac9898 100644 --- a/packages/browserslist-config-kolibri/package.json +++ b/packages/browserslist-config-kolibri/package.json @@ -1,6 +1,6 @@ { "name": "browserslist-config-kolibri", - "version": "0.16.0-dev.7", + "version": "0.16.1-dev.1", "description": "Browsers list configuration for Kolibri", "main": "index.js", "repository": "github.com/learningequality/kolibri", diff --git a/packages/eslint-plugin-kolibri/package.json b/packages/eslint-plugin-kolibri/package.json index 97bd968e82..66a53df236 100644 --- a/packages/eslint-plugin-kolibri/package.json +++ b/packages/eslint-plugin-kolibri/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-kolibri", - "version": "0.16.0-dev.7", + "version": "0.16.1-dev.1", "description": "Custom rules.", "author": "Learning Equality", "main": "lib/index.js", diff --git a/packages/kolibri-common/components/MeteredConnectionNotificationModal.vue b/packages/kolibri-common/components/MeteredConnectionNotificationModal.vue index e4d498d016..3943f77a03 100644 --- a/packages/kolibri-common/components/MeteredConnectionNotificationModal.vue +++ b/packages/kolibri-common/components/MeteredConnectionNotificationModal.vue @@ -90,8 +90,8 @@ // if we only include one of the keys for the extra_settings object client({ method: 'GET', url: this.settingsUrl }) .then(({ data }) => { - logging.log('mounted', isMetered); - logging.log(data); + logging.info('mounted', isMetered); + logging.info(data); this.extra_settings = data.extra_settings; this.selected = this.extra_settings.allow_download_on_metered_connection ? Options.USE_METERED diff --git a/packages/kolibri-core-for-export/package.json b/packages/kolibri-core-for-export/package.json index fbb843b419..7836906168 100644 --- a/packages/kolibri-core-for-export/package.json +++ b/packages/kolibri-core-for-export/package.json @@ -1,6 +1,6 @@ { "name": "kolibri", - "version": "0.16.0-dev.7", + "version": "0.16.1-dev.1", "description": "The Kolibri core API", "repository": "github.com/learningequality/kolibri", "author": "Learning Equality", @@ -33,7 +33,7 @@ "screenfull": "^5.2.0", "tinycolor2": "^1.6.0", "ua-parser-js": "^1.0.37", - "vue": "^2.6.14", + "vue": "2.6.14", "vue-intl": "3.1.0", "vue-meta": "^2.4.0", "vue-router": "^3.6.5", diff --git a/packages/kolibri-tools/lib/cli.js b/packages/kolibri-tools/lib/cli.js index 67788c24eb..5a7b161a2d 100755 --- a/packages/kolibri-tools/lib/cli.js +++ b/packages/kolibri-tools/lib/cli.js @@ -287,6 +287,10 @@ program } if (options.watchonly.length) { const unwatchedBundles = []; + // Watch core for changes if KDS option is provided; all KDS components are linked to core. + if (options.requireKdsPath && !options.watchonly.includes('core')) { + options.watchonly.push('core'); + } const findModuleName = bundleDatum => { return !options.watchonly.some(m => bundleDatum.module_path.includes(m)); }; @@ -366,7 +370,7 @@ program const Minimatch = require('minimatch').Minimatch; patternCheck = new Minimatch(options.pattern, {}); } - const glob = require('glob'); + const glob = require('./glob'); const { logging, lint, noChange } = require('./lint'); const chokidar = require('chokidar'); const watchMode = options.monitor; @@ -457,7 +461,7 @@ program if (!files.length) { program.command('compress').help(); } else { - const glob = require('glob'); + const glob = require('./glob'); const compressFile = require('./compress'); Promise.all( files.map(file => { diff --git a/packages/kolibri-tools/lib/glob.js b/packages/kolibri-tools/lib/glob.js new file mode 100644 index 0000000000..671527aa65 --- /dev/null +++ b/packages/kolibri-tools/lib/glob.js @@ -0,0 +1,35 @@ +const fastGlob = require('fast-glob'); + +const defaultOptions = { + ignore: [], + expandDirectories: true, + braceExpansion: true, + dot: false, + extglob: true, + globstar: true, + caseSensitiveMatch: true, +}; + +/** + * Small wrapper around glob dependency to make it easier to switch to a different library + */ +module.exports = { + /** + * Synchronously search for files that match a glob pattern + * @param {string|string[]} globPaths + * @param {object} options - see fast-glob options + * @returns {string[]} + */ + sync(globPaths, options = {}) { + if (Array.isArray(globPaths)) { + globPaths = [...new Set(globPaths)]; + } else { + globPaths = [globPaths]; + } + + return fastGlob.sync(globPaths, { + ...defaultOptions, + ...options, + }); + }, +}; diff --git a/packages/kolibri-tools/lib/i18n/SyncContext.js b/packages/kolibri-tools/lib/i18n/SyncContext.js index 527b571534..87014e84e9 100644 --- a/packages/kolibri-tools/lib/i18n/SyncContext.js +++ b/packages/kolibri-tools/lib/i18n/SyncContext.js @@ -1,10 +1,10 @@ // Import packages const fs = require('fs'); const path = require('path'); -const glob = require('glob'); const traverse = require('ast-traverse'); const get = require('lodash/get'); const vueCompiler = require('vue-template-compiler'); +const glob = require('../glob'); const logging = require('../logging'); const { insertContent } = require('../vueTools'); const { diff --git a/packages/kolibri-tools/lib/i18n/astUtils.js b/packages/kolibri-tools/lib/i18n/astUtils.js index 308e1d84c8..5a0540a262 100644 --- a/packages/kolibri-tools/lib/i18n/astUtils.js +++ b/packages/kolibri-tools/lib/i18n/astUtils.js @@ -8,7 +8,7 @@ const get = require('lodash/get'); const isPlainObject = require('lodash/isPlainObject'); const isString = require('lodash/isString'); const isArray = require('lodash/isArray'); -const { glob } = require('glob'); +const glob = require('../glob'); const logging = require('../logging'); const { resolve } = require('../alias_import_resolver'); const { CONTEXT_LINE } = require('./constants'); diff --git a/packages/kolibri-tools/lib/i18n/utils.js b/packages/kolibri-tools/lib/i18n/utils.js index f82ef57b3c..912aea9c7e 100644 --- a/packages/kolibri-tools/lib/i18n/utils.js +++ b/packages/kolibri-tools/lib/i18n/utils.js @@ -1,10 +1,10 @@ const fs = require('fs'); const path = require('path'); -const glob = require('glob'); const intersection = require('lodash/intersection'); const { parse } = require('csv-parse/sync'); const { lint } = require('kolibri-tools/lib/lint'); const { addAliases, resetAliases } = require('kolibri-tools/lib/alias_import_resolver'); +const glob = require('../glob'); const logging = require('../logging'); /* diff --git a/packages/kolibri-tools/package.json b/packages/kolibri-tools/package.json index 984394b3e2..cdf9bbd226 100644 --- a/packages/kolibri-tools/package.json +++ b/packages/kolibri-tools/package.json @@ -1,6 +1,6 @@ { "name": "kolibri-tools", - "version": "0.16.0-dev.7", + "version": "0.16.1-dev.1", "description": "Tools for building Kolibri frontend plugins", "main": "lib/cli.js", "repository": "github.com/learningequality/kolibri", @@ -24,7 +24,7 @@ "babel-core": "7.0.0-bridge.0", "babel-jest": "^29.7.0", "babel-loader": "^9.1.3", - "browserslist-config-kolibri": "0.16.0-dev.7", + "browserslist-config-kolibri": "0.16.1-dev.1", "chalk": "4.1.2", "check-node-version": "^4.2.1", "chokidar": "^3.6.0", @@ -42,13 +42,13 @@ "eslint-config-vue": "^2.0.2", "eslint-plugin-import": "^2.29.1", "eslint-plugin-jest": "^23.3.0", - "eslint-plugin-kolibri": "0.16.0-dev.7", + "eslint-plugin-kolibri": "0.16.1-dev.1", "eslint-plugin-jest-dom": "^5.1.0", "eslint-plugin-vue": "^7.3.0", "espree": "10.0.1", "esquery": "^1.5.0", "express": "^4.18.3", - "glob": "^10.3.10", + "fast-glob": "^3.3.2", "htmlhint": "^1.1.4", "ini": "^4.1.2", "jest": "^29.7.0", @@ -89,7 +89,7 @@ "vue-jest": "^3.0.0", "vue-loader": "^15.11.1", "vue-style-loader": "^4.1.3", - "vue-template-compiler": "^2.6.11", + "vue-template-compiler": "2.6.14", "webpack": "^5.90.3", "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.0.4", diff --git a/yarn.lock b/yarn.lock index 2086de5d3e..833adf2bfa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5550,7 +5550,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.2.12, fast-glob@^3.2.9: +fast-glob@^3.2.12, fast-glob@^3.2.9, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -5981,7 +5981,7 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@^10.3.10, glob@^10.3.7: +glob@^10.3.7: version "10.3.10" resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.10.tgz#0351ebb809fd187fe421ab96af83d3a70715df4b" integrity sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g== @@ -7139,9 +7139,9 @@ istanbul-reports@^3.1.3: istanbul-lib-report "^3.0.0" jackspeak@^2.3.5: - version "2.3.5" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.5.tgz#443f237f9eeeb0d7c6ec34835ef5289bb4acb068" - integrity sha512-Ratx+B8WeXLAtRJn26hrhY8S1+Jz6pxPMrkrdkgb/NstTNiqMhX0/oFVu5wX+g5n6JlEu2LPsDJmY8nRP4+alw== + version "2.3.6" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" + integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== dependencies: "@isaacs/cliui" "^8.0.2" optionalDependencies: @@ -8253,9 +8253,9 @@ minimatch@^5.0.1: brace-expansion "^2.0.1" minimatch@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.1.tgz#8a555f541cf976c622daf078bb28f29fb927c253" - integrity sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w== + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== dependencies: brace-expansion "^2.0.1" @@ -8345,9 +8345,9 @@ minipass@^4.0.0: yallist "^4.0.0" "minipass@^5.0.0 || ^6.0.2 || ^7.0.0": - version "7.0.1" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.1.tgz#dff63464407cd8b83d7f008c0f116fa8c9b77ebf" - integrity sha512-NQ8MCKimInjVlaIqx51RKJJB7mINVkLTJbsZKmto4UAAOC/CWXES8PGaOgoBZyqoUsUA/U3DToGK7GJkkHbjJw== + version "7.0.4" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" + integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== minizlib@^2.0.0, minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" @@ -10186,9 +10186,9 @@ screenfull@^5.2.0: integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA== scrollparent@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/scrollparent/-/scrollparent-2.0.1.tgz#715d5b9cc57760fb22bdccc3befb5bfe06b1a317" - integrity sha1-cV1bnMV3YPsivczDvvtb/gaxoxc= + version "2.1.0" + resolved "https://registry.yarnpkg.com/scrollparent/-/scrollparent-2.1.0.tgz#6cae915c953835886a6ba0d77fdc2bb1ed09076d" + integrity sha512-bnnvJL28/Rtz/kz2+4wpBjHzWoEzXhVg/TE8BeVGJHUqE8THNIRnDxDWMktwM+qahvlRdvlLdsQfYe+cuqfZeA== scss-parser@^1.0.6: version "1.0.6" @@ -10395,9 +10395,9 @@ signal-exit@^3.0.7: integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== signal-exit@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.0.1.tgz#96a61033896120ec9335d96851d902cc98f0ba2a" - integrity sha512-uUWsN4aOxJAS8KOuf3QMyFtgm1pkb6I+KRZbRF/ghdf5T7sM+B1lLLzPDxswUjkmHyxQAVzEgG35E3NzDM9GVw== + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== simple-markdown@^0.2.1: version "0.2.2" @@ -10796,9 +10796,9 @@ strip-ansi@^5.1.0, strip-ansi@^5.2.0: ansi-regex "^4.1.0" strip-ansi@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" - integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw== + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== dependencies: ansi-regex "^6.0.1" @@ -11734,12 +11734,12 @@ vue-meta@^2.4.0: dependencies: deepmerge "^4.2.2" -vue-observe-visibility@^0.4.1: +vue-observe-visibility@^0.4.4: version "0.4.6" resolved "https://registry.yarnpkg.com/vue-observe-visibility/-/vue-observe-visibility-0.4.6.tgz#878cb8ebcf3078e40807af29774e97105ebd519e" integrity sha512-xo0CEVdkjSjhJoDdLSvoZoQrw/H2BlzB5jrCBKGZNXN2zdZgMuZ9BKrxXDjNP2AxlcCoKc8OahI3F3r3JGLv2Q== -vue-resize@^0.4.4: +vue-resize@^0.4.5: version "0.4.5" resolved "https://registry.yarnpkg.com/vue-resize/-/vue-resize-0.4.5.tgz#4777a23042e3c05620d9cbda01c0b3cc5e32dcea" integrity sha512-bhP7MlgJQ8TIkZJXAfDf78uJO+mEI3CaLABLjv0WNzr4CcGRGPIAItyWYnP6LsPA4Oq0WE+suidNs6dgpO4RHg== @@ -11757,7 +11757,7 @@ vue-style-loader@^4.1.0, vue-style-loader@^4.1.3: hash-sum "^1.0.2" loader-utils "^1.0.2" -vue-template-compiler@^2.6.11: +vue-template-compiler@2.6.14: version "2.6.14" resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz#a2f0e7d985670d42c9c9ee0d044fed7690f4f763" integrity sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g== @@ -11770,21 +11770,21 @@ vue-template-es2015-compiler@^1.6.0, vue-template-es2015-compiler@^1.9.0: resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825" integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw== -vue-virtual-scroller@0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/vue-virtual-scroller/-/vue-virtual-scroller-0.12.0.tgz#42f8fd7b299cd4d4fd1f5e14676792c229b890e2" - integrity sha512-01hKN99psA9Hq6zJajQc8H63sgqjpytHC8IyO+WKm6JzdVi8LeJQQqMrz5iUAFW6ZDGk+hvxaAqqcnJv+X8vYg== +vue-virtual-scroller@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vue-virtual-scroller/-/vue-virtual-scroller-1.1.2.tgz#b8a6362f177abf3f2149ce1eac18013c71fe353b" + integrity sha512-SkUyc7QHCJFB5h1Fya7LxVizlVzOZZuFVipBGHYoTK8dwLs08bIz/tclvRApYhksaJIm/nn51inzO2UjpGJPMQ== dependencies: scrollparent "^2.0.1" - vue-observe-visibility "^0.4.1" - vue-resize "^0.4.4" + vue-observe-visibility "^0.4.4" + vue-resize "^0.4.5" vue2-teleport@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/vue2-teleport/-/vue2-teleport-1.0.1.tgz#1b7f9f69c1223f522cf6cd81c39b8d6019e1cf66" integrity sha512-hbY/Q0x8qXGFxo6h4KU4YYesUcN+uUjliqqC0PoNSgpcbS2QRb3qXi+7XMTgLYs0a8i7o1H6Mu43UV4Vbgkhgw== -vue@^2.6.14: +vue@2.6.14: version "2.6.14" resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.14.tgz#e51aa5250250d569a3fbad3a8a5a687d6036e235" integrity sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==