diff --git a/build/build.node.ts b/build/build.node.ts index 5da9665886..97059586fa 100644 --- a/build/build.node.ts +++ b/build/build.node.ts @@ -7,24 +7,24 @@ namespace $ { @ $mol_mem_key static root( [ root, paths ] : [root: string, paths: readonly string[] ] ) { return this.make({ - root : ()=> $mol_file.absolute( root ) , + root : ()=> this.$.$mol_file.root( root ) , paths: $mol_const(paths) }) } static relative( root : string, paths: readonly string[] ) { - return $mol_build.root( [ $mol_file.relative( root ).path(), paths ]) + return this.$.$mol_build.root( [ $mol_file.relative( root ).path(), paths ]) } @ $mol_mem server() { - return $mol_build_server.make({ + return this.$.$mol_build_server.make({ build : $mol_const( this ) , }) } root() { - return $mol_file.relative( '.' ) + return this.$.$mol_file.relative( '.' ) } paths() { @@ -63,11 +63,13 @@ namespace $ { const name = file.name() const tree = this.$.$mol_tree2_from_string( file.text() , file.path() ) + const dir = file.parent().path() let content = '' for( const step of tree.select( 'build' , null ).kids ) { - const res = this.$.$mol_run.spawn( { command: step.text(), dir: file.parent().path(), dirty: true } ).stdout.toString().trim() + const res = this.$.$mol_file.watch_off(() => this.$.$mol_run.spawn( { command: step.text(), dir } ), dir ) + .stdout.toString().trim() if( step.type ) content += `let ${ step.type } = ${ JSON.stringify( res ) }` } @@ -92,7 +94,7 @@ namespace $ { const js_map = target.resolve( js.name() + '.map' ) const dts = target.resolve( source.name() + '.d.ts' ) const dts_map = target.resolve( dts.name() + '.map' ) - + const js_text = this.$.$mol_tree2_js_to_text( this.$.$mol_view_tree2_to_js( tree ) ) js.text( this.$.$mol_tree2_text_to_string( js_text ) + '\n//# sourceMappingURL=' + js_map.relate( target ) ) js_map.text( JSON.stringify( this.$.$mol_tree2_text_to_sourcemap( js_text ), null, '\t' ) ) @@ -118,7 +120,7 @@ namespace $ { const file = $mol_file.absolute( path ) const name = file.name() const script = file.parent().resolve( `-css/${ name }.ts` ) - + const id = file.relate( this.root() ) const styles = file.text() const code = 'namespace $ { $'+`mol_style_attach( ${ JSON.stringify( id ) },\n ${ JSON.stringify( styles ) }\n) }` @@ -150,7 +152,7 @@ namespace $ { @ $mol_mem_key mods( [ path , exclude ] : [ path : string , exclude? : readonly string[] ] ) { - + const mods : $mol_file[] = [] for (const child of this.sorted_sub(path)) { @@ -788,9 +790,12 @@ namespace $ { } + @ $mol_action + protected now() { return Date.now() } + @ $mol_mem_key bundleJS( { path , exclude , bundle } : { path : string , exclude : readonly string[] , bundle : string } ) : $mol_file[] { - const start = Date.now() + const start = this.now() var pack = $mol_file.absolute( path ) var targetJS = pack.resolve( `-/${bundle}.js` ) @@ -850,7 +855,7 @@ namespace $ { @ $mol_mem_key bundleMJS( { path , exclude , bundle } : { path : string , exclude : readonly string[] , bundle : string } ) : $mol_file[] { - const start = Date.now() + const start = this.now() const [ targetJS, targetJSMap ] = this.bundleJS({ path, exclude, bundle }) if (! targetJS) return [] @@ -865,7 +870,7 @@ namespace $ { @ $mol_mem_key bundleAuditJS( { path , exclude , bundle } : { path : string , exclude : readonly string[] , bundle : string } ) : $mol_file[] { - const start = Date.now() + const start = this.now() var pack = $mol_file.absolute( path ) var target = pack.resolve( `-/${bundle}.audit.js` ) @@ -904,7 +909,7 @@ namespace $ { @ $mol_mem_key bundle_test_js([ path , exclude , bundle ] : [ path : string , exclude : readonly string[] , bundle : string ]) { - const start = Date.now() + const start = this.now() const pack = $mol_file.absolute( path ) const root = this.root() @@ -962,7 +967,15 @@ namespace $ { bundleAndRunTestJS( { path , exclude , bundle } : { path : string , exclude : readonly string[] , bundle : string } ) : $mol_file[] { const [ target , targetMap ] = this.bundle_test_js([ path, exclude, bundle ]) if( bundle === 'node' ) { - this.$.$mol_run.spawn( { command: ['node', '--enable-source-maps', '--trace-uncaught', target.relate( this.root() ) ], dir: this.root().path() } ) + const dir = this.root().path() + + this.$.$mol_file.watch_off(() => + this.$.$mol_run.spawn( { + command: ['node', '--enable-source-maps', '--trace-uncaught', target.relate( this.root() ) ], + dir + } ), + dir + ) } return [ target , targetMap ] @@ -971,7 +984,7 @@ namespace $ { @ $mol_mem_key bundleTestHtml( path: string ) : $mol_file[] { - const start = Date.now() + const start = this.now() const pack = $mol_file.absolute( path ) const source = pack.resolve( 'index.html' ) @@ -1005,7 +1018,7 @@ namespace $ { @ $mol_mem_key bundleDTS( { path , exclude , bundle } : { path : string , exclude? : readonly string[] , bundle : string } ) : $mol_file[] { - const start = Date.now() + const start = this.now() var pack = $mol_file.absolute( path ) var target = pack.resolve( `-/${bundle}.d.ts` ) @@ -1033,7 +1046,7 @@ namespace $ { @ $mol_mem_key bundleViewTree( { path , exclude , bundle } : { path : string , exclude? : readonly string[] , bundle : string } ) : $mol_file[] { - const start = Date.now() + const start = this.now() var pack = $mol_file.absolute( path ) var target = pack.resolve( `-/${bundle}.view.tree` ) @@ -1052,9 +1065,8 @@ namespace $ { @ $mol_mem_key bundleMetaTree( { path , exclude , bundle } : { path : string , exclude? : readonly string[] , bundle : string } ) : $mol_file[] { - const start = Date.now() + const start = this.now() var pack = $mol_file.absolute( path ) - var target = pack.resolve( `-/${bundle}.meta.tree` ) const sortedPaths = this.graph( [path , exclude ] ).sorted @@ -1100,7 +1112,7 @@ namespace $ { @ $mol_mem_key bundleReadmeMd( [ path , exclude ] : [ path : string , exclude : readonly string[] ] ) : $mol_file[] { - const start = Date.now() + const start = this.now() const root = this.root() const pack = $mol_file.absolute( path ) @@ -1130,7 +1142,7 @@ namespace $ { @ $mol_mem_key bundlePackageJSON( [ path , exclude ] : [ path : string , exclude : readonly string[] ] ) : $mol_file[] { - const start = Date.now() + const start = this.now() var pack = $mol_file.absolute( path ) const source = pack.resolve( `package.json` ) @@ -1214,7 +1226,7 @@ namespace $ { const targets : $mol_file[] = [] - const start = Date.now() + const start = this.now() const html = pack.resolve( 'index.html' ) const tree = pack.resolve( 'index.xml.tree' ) const target = pack.resolve( '-/index.html' ) @@ -1258,10 +1270,10 @@ namespace $ { } return } - const start = Date.now() + const start = this.now() const target = file.clone(pack.resolve( `-/${ file.relate( root ) }` ).path()) - + if (! target) return targets.push( target ) this.logBundle( target , Date.now() - start ) } @@ -1279,7 +1291,7 @@ namespace $ { @ $mol_mem_key bundleCordova( [ path , exclude ] : [ path : string , exclude? : readonly string[] ] ) : $mol_file[] { - const start = Date.now() + const start = this.now() const pack = $mol_file.absolute( path ) const cordovaOut = pack.resolve( '-' ) const cordova = pack.resolve( '-cordova' ) @@ -1309,7 +1321,7 @@ namespace $ { bundleCSS( { path , exclude , bundle } : { path : string , exclude? : readonly string[] , bundle : string } ) : $mol_file[] { if( bundle === 'node' ) return [] - const start = Date.now() + const start = this.now() var pack = $mol_file.absolute( path ) var sources = [] as $mol_file[] // this.sourcesCSS( [ path , exclude ] ) @@ -1368,7 +1380,7 @@ namespace $ { ) const targets = Object.keys( locales ).map( lang => { - const start = Date.now() + const start = this.now() const target = pack.resolve( `-/${bundle}.locale=${ lang }.json` ) const locale = locales[ lang ] @@ -1407,7 +1419,7 @@ namespace $ { @ $mol_mem_key bundleDepsJSON( { path , exclude , bundle } : { path : string , exclude? : readonly string[] , bundle : string } ) : $mol_file[] { - const start = Date.now() + const start = this.now() const pack = $mol_file.absolute( path ) const list = this.sourcesAll( [ path , exclude ] ) diff --git a/build/ensure/git/git.ts b/build/ensure/git/git.ts index bda0eaae6f..7df62fc870 100644 --- a/build/ensure/git/git.ts +++ b/build/ensure/git/git.ts @@ -15,7 +15,9 @@ namespace $ { } protected override pull_run(dir: string) { - const out = this.$.$mol_run.spawn({ command: 'git rev-parse --abbrev-ref --symbolic-full-name HEAD', dir, dirty: true }) + const out = this.$.$mol_run.spawn({ + command: 'git rev-parse --abbrev-ref --symbolic-full-name HEAD', dir, + }) const current_branch = out.stdout.toString().trim() // когда не на ветке - не надо пулить, например сборка во время git bisect if (! current_branch) return false @@ -32,7 +34,7 @@ namespace $ { } const timeout = this.pull_timeout() - this.$.$mol_run.spawn( { command, dir, timeout, dirty: true }).stdout.toString().trim() + this.$.$mol_run.spawn( { command, dir, timeout }).stdout.toString().trim() return true } @@ -69,7 +71,7 @@ namespace $ { const repo = this.repo(dir) if (! repo) return 'master' - const res = this.$.$mol_run.spawn( { command: ['git', 'remote', 'show', repo.url ], dir, dirty: true } ) + const res = this.$.$mol_run.spawn( { command: ['git', 'remote', 'show', repo.url ], dir } ) return res.stdout.toString().match( /HEAD branch: (.*?)\n/ )?.[1] ?? 'master' } @@ -77,15 +79,14 @@ namespace $ { protected override init(dir: string) { const repo = this.repo(dir) if (! repo) throw new Error(`"${dir}" not a repo`) + const { url, branch } = repo + this.$.$mol_run.spawn( { command: ['git', 'init'], dir } ) - this.$.$mol_run.spawn( { command: ['git', 'init'], dir, dirty: true } ) - const branch = repo.branch ?? this.branch_remote(dir) - - const command = ['git', 'remote', 'add', '--track', branch, 'origin' , repo.url ] - this.$.$mol_run.spawn( { command, dir, dirty: true } ) - // this.$.$mol_run.spawn( { command: [ 'git', 'pull', 'origin', branch ], dir, dirty: true } ) - this.$.$mol_run.spawn( { command: [ 'git', 'checkout', branch ], dir, dirty: true } ) + const branch_norm = branch ?? this.branch_remote(dir) + this.$.$mol_run.spawn( { command: ['git', 'remote', 'add', '--track', branch_norm, 'origin' , url ], dir } ) + this.$.$mol_run.spawn( { command: [ 'git', 'pull', 'origin', branch_norm ], dir } ) + return null } @@ -103,7 +104,7 @@ namespace $ { ] const dir = this.root().path() - this.$.$mol_run.spawn( { command, dir, dirty: true } ) + this.$.$mol_run.spawn( { command, dir } ) return null } diff --git a/build/ensure/vcs/vcs.ts b/build/ensure/vcs/vcs.ts index fc56777a9f..47e01209f0 100644 --- a/build/ensure/vcs/vcs.ts +++ b/build/ensure/vcs/vcs.ts @@ -45,10 +45,9 @@ namespace $ { if (this.pull_disabled) return false try { - return this.pull_run(dir) + return this.$.$mol_file.watch_off(() => this.pull_run(dir), dir) } catch (e) { if (e instanceof $mol_run_error && e.cause.timeout_kill) { - this.pull_disabled = true this.$.$mol_log3_warn({ place: `${this}.pull()`, @@ -56,6 +55,7 @@ namespace $ { hint: 'Check connection', }) + this.pull_disabled = true return true } @@ -75,18 +75,24 @@ namespace $ { if( mod.exists()) { if (! this.inited(path)) { if (! this.repo(path) ) return false - this.init(path) + + this.$.$mol_file.watch_off(() => this.init(path), path) } + this.pull( path ) - mod.reset() - for ( const sub of mod.sub() ) sub.reset() + + // mod.reset() + // for ( const sub of mod.sub() ) sub.reset() return true } if (this.repo(path)) { - this.clone(path) - mod.reset() + + this.$.$mol_file.watch_off(() => this.clone(path), path) + + // mod.reset() + // for ( const sub of mod.sub() ) sub.reset() return true } diff --git a/build/server/server.node.ts b/build/server/server.node.ts index 9bb23f79fa..22cd30e70c 100644 --- a/build/server/server.node.ts +++ b/build/server/server.node.ts @@ -12,6 +12,7 @@ namespace $ { ) => boolean | void ) { const pending_urls = {} as Record | undefined> + const wrapped = $mol_wire_async(mdl) const cb = async ( req : typeof $node.express.request , @@ -19,13 +20,14 @@ namespace $ { next : (err?: unknown) => any ) => { try { - const call_next = await $mol_wire_async(mdl)(req, res) + const call_next = await wrapped(req, res) if (! call_next) return await Promise.resolve() delete pending_urls[req.url] next() } catch (err) { delete pending_urls[req.url] + console.error(err) next(err) } } diff --git a/file/file.node.ts b/file/file.node.ts index a729d32167..a9db2d69b3 100644 --- a/file/file.node.ts +++ b/file/file.node.ts @@ -43,10 +43,10 @@ namespace $ { static relative( path : string ) { return this.absolute( $node.path.resolve( this.base, path ).replace( /\\/g , '/' ) ) } - - @ $mol_mem - override watcher(reset?: null) { - const watcher = $node.chokidar.watch( this.path() , { + + @ $mol_mem_key + static watcher(path: string) { + const watcher = $node.chokidar.watch( path , { persistent : true , ignored: path => /([\/\\]\.|___$)/.test( path ), depth : 0 , @@ -57,7 +57,7 @@ namespace $ { } ) watcher - .on( 'all' , (type, path) => this.watch_event(type, path) ) + .on( 'all' , (type, path) => this.changed_add(type, path) ) .on( 'error' , $mol_fail_log ) return { @@ -68,63 +68,47 @@ namespace $ { } - protected watch_event(type: string, path: string) { - const file = $mol_file.relative( path.replace( /\\/g , '/' ) ) - const parent = type === 'change' ? this : file.parent() - file.reset_schedule() - parent.reset_schedule() - } - - static reset_changed() { - this.$.$mol_run.lock_run(() => super.reset_changed()) - } + override watcher() { return this.$.$mol_file_node.watcher(this.path()) } - @ $mol_mem - override stat(next? : $mol_file_stat | null, virt?: 'virt') { - let stat = next - const path = this.path() + protected fs() { return $mol_wire_sync($node.fs.promises) } - this.parent().watcher() - - if( virt ) return next ?? null - + @ $mol_action + protected override info( path: string ) { try { - stat = next ?? stat_convert($node.fs.statSync( path, { throwIfNoEntry: false } )) + return stat_convert($node.fs.statSync(path)) } catch( error: any ) { if (this.$.$mol_fail_catch(error)) { - // For node < 14.7.0 compatible with throwIfNoEntry: false above if (error.code === 'ENOENT') return null error.message += '\n' + path + this.$.$mol_fail_hidden(error) } - return this.$.$mol_fail_hidden(error) } - - return stat + return null } - @ $mol_mem - override ensure() { + @ $mol_action + protected override ensure() { const path = this.path() - try { $node.fs.mkdirSync( path, { recursive: true } ) + return null } catch( e: any ) { if (this.$.$mol_fail_catch(e)) { if (e.code === 'EEXIST') return null e.message += '\n' + path + this.$.$mol_fail_hidden(e) } - this.$.$mol_fail_hidden(e) } } @ $mol_action - override copy(to: string) { + protected override copy(to: string) { $node.fs.copyFileSync(this.path(), to) } @ $mol_action - override drop() { + protected override drop() { $node.fs.unlinkSync( this.path() ) } @@ -156,7 +140,6 @@ namespace $ { if (this.$.$mol_fail_catch(error)) { error.message += '\n' + path } - return this.$.$mol_fail_hidden( error ) } @@ -189,23 +172,21 @@ namespace $ { return next } - @ $mol_mem - override sub() : $mol_file[] { - if (! this.exists() ) return [] - if ( this.type() !== 'dir') return [] + protected override kids() { const path = this.path() - this.stat() - try { - return $node.fs.readdirSync( path ) + const kids = $node.fs.readdirSync( path ) .filter( name => !/^\.+$/.test( name ) ) .map( name => this.resolve( name ) ) + + return kids } catch( e: any ) { if (this.$.$mol_fail_catch(e)) { + if (e.code === 'ENOENT') return [] e.message += '\n' + path } - return this.$.$mol_fail_hidden(e) + $mol_fail_hidden(e) } } @@ -217,7 +198,7 @@ namespace $ { return $node.path.relative( base.path() , this.path() ).replace( /\\/g , '/' ) } - override append( next : Uint8Array | string ) { + protected override append( next : Uint8Array | string ) { const path = this.path() try { $node.fs.appendFileSync( path , next ) diff --git a/file/file.ts b/file/file.ts index ec8e84cdc8..62507eb084 100644 --- a/file/file.ts +++ b/file/file.ts @@ -35,56 +35,181 @@ namespace $ { return this.resolve( '..' ) } - stat(next? : $mol_file_stat | null, virt?: 'virt'): null | $mol_file_stat { - return null + @ $mol_mem + stat(next? : $mol_file_stat | null, virt?: 'virt') { + + const path = this.path() + const parent = this.parent() + parent.watcher() + + // Отслеживать проверку наличия родительской папки не стоит до корня диска + // Лучше ограничить mam-ом + const root = this.$.$mol_file.watch_root ?? this + if ( this !== root ) { + // Если родитель удалился, надо ресетнуть все дочерние на любой глубине + // Родитель может удалиться, потом создасться, а дочерняя папка только удалиться. + // Поэтому parent.exists() не запустит перевычисления + // parent.version() меняется не только при удалении, будет ложное срабатывание + // события вотчера addDir сбрасывает только parent.sub(), а parent.version() может остаться та же + // тогда дочерний не перзапустится + // Если addDir будет сбрасывать parent.version(), то будет лишний раз перевычислен parent, хоть и он сам не поменялся + parent.version() + // parent.sub_version() + } + + if( virt ) return next ?? null + + return next ?? this.info(path) } - reset() { this.stat(null) } - reset_schedule() { return this.$.$mol_file.reset_schedule(this.path()) } + protected static changed = new Set<$mol_file> + protected static added = new Set<$mol_file> + + protected static frame = null as null | $mol_after_timeout + + protected static changed_add(type: 'addDir' | 'unlinkDir' | 'add' | 'change' | 'unlink', path: string) { + const file = this.$.$mol_file.relative( path.at(-1) === '/' ? path.slice(0, -1) : path ) - protected static changed_paths = new Set() + if (type === 'add') { + // добавился файл - у parent надо обновить список sub, если он был заюзан + this.added.add(file) + } + + if (type === 'change' || type === 'unlink') { + // обновился или удалился файл - ресетим + this.changed.add(file) + } + + if ( type === 'addDir' ) { + // добавилась папка, у parent обновляем список директорий в sub + // дочерние ресетим + // версию папки не меняем, т.к. иначе выполнится логика, связанная + this.added.add(file) + } - static reset_schedule(path: string) { - if (! this.changed_paths.size) new this.$.$mol_after_frame(()=> $mol_wire_async(this).reset_changed()) - this.changed_paths.add(path) + if ( type === 'unlinkDir') { + // удалилась папка, ресетим ее + // stat у всех дочерних обновится сам, т.к. связан с parent.version() + this.changed.add(file) + } + + if (! this.watching) return + + this.frame?.destructor() + this.frame = new this.$.$mol_after_timeout(500, () => { + if (! this.watching) return + this.watching = false + $mol_wire_async(this).flush() + } ) } - static reset_changed() { - this.$.$mol_log3_rise({ - place: `${this}.reset_changed`, - message: 'Watch reset', - paths: [... this.changed_paths], - }) - for (const path of this.changed_paths) { - try { - this.absolute(path).reset() - } catch (e) { - if ($mol_fail_catch(e)) $mol_fail_log(e) - } + @ $mol_mem + static flush_counter(reset?: null): number { + return 1 + ( $mol_wire_probe(() => this.flush_counter()) ?? 0 ) + } + + @ $mol_mem + static flusher() { + try { + // this.flush() + } catch (e) { + this.$.$mol_fail_log(e) } - this.changed_paths.clear() + } + + @ $mol_action + static flush() { + // this.flush_counter() + // Пока flush работает, вотчер сюда не заходит, но может добавлять новые изменения + // на каждом перезапуске они применятся + // Пока run выполняется, изменения накапливаются, в конце run вызывается flush + // Пока применяются изменения, run должен ожидать конца flush + + for (const file of this.added) { + const parent = file.parent() + if ($mol_wire_probe(() => parent.sub())) parent.sub(null) + file.reset() + // file.sub_version(null) + } + + this.changed.forEach(file => file.reset()) + + this.added.clear() + this.changed.clear() + + // Выставляем обратно в true, что б watch мог зайти сюда + this.watching = true } + @ $mol_mem + protected sub_version(reset?: null): number { + return 1 + ( $mol_wire_probe(() => this.sub_version()) ?? 0 ) + } + + protected static watching = true + + // @ $mol_action + static watch_off(cb: () => Result, path: string) { + try { + this.watching = false + const result = cb() + // this.flush_counter(null) + // watch запаздывает и событие может прилететь через 3 сек после окончания git pull + this.$.$mol_file.absolute(path).reset() + this.flush() + return result + } catch (e) { + if ( ! $mol_promise_like(e) ) this.flush() + $mol_fail_hidden(e) + } + } + + reset() { + this.stat( null ) + } + + @ $mol_mem + modified() { return this.stat()?.mtime ?? null } + @ $mol_mem version() { - return this.stat()?.mtime.getTime().toString( 36 ).toUpperCase() ?? '' + return this.modified()?.getTime().toString( 36 ).toUpperCase() ?? '' } - ensure() {} - drop() {} - copy(to: string) {} + protected info( path: string ) { return null as null | $mol_file_stat } + protected ensure() {} + protected drop() {} + protected copy(to: string) {} @ $mol_mem_key clone(to: string) { - this.stat() - const file = this.$.$mol_file.absolute(to) - file.parent().ensure() - this.copy(to) - file.reset() - return file + if (! this.exists() ) return null + + const target = this.$.$mol_file.absolute(to) + + try { + this.version() + target.parent().exists(true) + this.copy(to) + target.reset() + return target + } catch (error) { + if ( $mol_fail_catch(error)) { + console.error(error) + } + } + return null + } + + protected static watch_root = null as null | $mol_file + + static root( path: string) { + this.watch_root = this.absolute( path ) + return this.watch_root } - watcher(reset?: null) { + + watcher() { console.warn('$mol_file_web.watcher() not implemented') return { @@ -95,7 +220,7 @@ namespace $ { @ $mol_mem exists( next? : boolean ) { - let exists = Boolean( this.stat() ) + const exists = Boolean( this.stat() ) if( next === undefined ) return exists if( next === exists ) return exists @@ -103,11 +228,14 @@ namespace $ { if( next ) { this.parent().exists( true ) this.ensure() - } else { - this.drop() + this.reset() + return next } + + this.drop() + // удалили директорию, все дочерние потеряли актуальность this.reset() - + return next } @@ -128,8 +256,13 @@ namespace $ { @ $mol_mem buffer( next? : Uint8Array ) { return next ?? new Uint8Array } - @ $mol_mem text(next?: string, virt?: 'virt') { + if (next !== undefined) this.version() + return this.text_int(next, virt) + } + + @ $mol_mem + text_int(next?: string, virt?: 'virt') { if( virt ) { const now = new Date this.stat( { @@ -137,20 +270,34 @@ namespace $ { size: 0, atime: now, mtime: now, - ctime: now, + ctime: now, }, 'virt' ) return next! } + if( next === undefined ) { - return $mol_charset_decode( this.buffer( undefined ) ) + return $mol_charset_decode( this.buffer( ) ) } else { - const buffer = next === undefined ? undefined : $mol_charset_encode( next ) + const buffer = $mol_charset_encode( next ) this.buffer( buffer ) return next } } - sub() { return [] as $mol_file[] } + @ $mol_mem + sub(reset?: null) { + if (! this.exists() ) return [] + if ( this.type() !== 'dir') return [] + + this.stat() + + // Если дочерний file удалился, список надо обновить + return this.kids().filter(file => file.exists()) + } + + protected kids() { + return [] as readonly $mol_file[] + } resolve(path: string): $mol_file { throw new Error('implement') @@ -160,7 +307,7 @@ namespace $ { throw new Error('implement') } - append( next : Uint8Array | string ) {} + protected append( next : Uint8Array | string ) {} find( include? : RegExp , @@ -202,4 +349,6 @@ namespace $ { } } + + $mol_file.flusher() } diff --git a/file/file.web.ts b/file/file.web.ts index 5bc6c9d160..fd9d61687c 100644 --- a/file/file.web.ts +++ b/file/file.web.ts @@ -21,27 +21,6 @@ namespace $ { return new Uint8Array(response.buffer()) } - @ $mol_mem - override stat( next? : $mol_file_stat, virt?: 'virt' ) { - let stat = next - if (next === undefined) { - const content = this.text() - // @todo взять дату из хедеров фетча, когда file.web будет переписан на webdav - const ctime = new Date() - stat = { - type: 'file', - size: content.length, - ctime, - atime: ctime, - mtime: ctime - } - } - - this.parent().watcher() - - return stat! - } - override resolve( path : string ) { let res = this.path() + '/' + path @@ -54,16 +33,16 @@ namespace $ { return ( this.constructor as typeof $mol_file_web ).absolute( res ) } - override ensure() { + protected override ensure() { throw new Error('$mol_file_web.ensure() not implemented') } - override drop() { + protected override drop() { throw new Error('$mol_file_web.drop() not implemented') - } + } @ $mol_mem - override sub() : $mol_file[] { + protected override kids() : readonly $mol_file[] { throw new Error('$mol_file_web.sub() not implemented') } diff --git a/lock/lock.ts b/lock/lock.ts deleted file mode 100644 index eb6659213a..0000000000 --- a/lock/lock.ts +++ /dev/null @@ -1,44 +0,0 @@ -namespace $ { - export class $mol_lock extends $mol_object { - protected promise = null as null | Promise - - async lock_async() { - let next = () => {} - let destructed = false - const task = $mol_wire_auto() - if (! task) return next - - const destructor = task.destructor.bind(task) - task.destructor = ()=> { - destructor() - destructed = true - next() - } - - let promise - - do { - promise = this.promise - await promise - if (destructed) return next - } while (promise !== this.promise) - - this.promise = new Promise(done => { next = done }) - return next - } - - lock() { return $mol_wire_sync(this).lock_async() } - - run(cb: () => Result) { - const unlock = this.lock() - try { - const result = cb() - unlock() - return result - } catch(e) { - if (! $mol_promise_like(e)) unlock() - $mol_fail_hidden(e) - } - } - } -} diff --git a/run/run.node.ts b/run/run.node.ts index f761937b99..f3e494709e 100644 --- a/run/run.node.ts +++ b/run/run.node.ts @@ -27,27 +27,20 @@ namespace $ { command : readonly string[] | string dir : string timeout?: number - dirty?: boolean env?: Record } export class $mol_run extends $mol_object { - protected static lock = new $mol_lock - - static lock_run(cb: () => Result) { return this.lock.run(cb) } - static spawn(options: $mol_run_options) { const sync = ! this.$.$mol_env()['MOL_RUN_ASYNC'] || ! Boolean($mol_wire_auto()) const env = options.env ?? this.$.$mol_env() - const cb = () => $mol_wire_sync(this).spawn_async( { ...options, sync, env } ) - - return ! sync && options.dirty ? this.lock_run(cb) : cb() + return $mol_wire_sync(this).spawn_async( { ...options, sync, env } ) } static spawn_async( - { dir, sync, timeout, command, env, dirty }: $mol_run_options & { sync?: boolean } + { dir, sync, timeout, command, env }: $mol_run_options & { sync?: boolean } ) { const args_raw = typeof command === 'string' ? command.split( ' ' ) : command const [ app, ...args ] = args_raw