Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bug] NextJs window is not defined on build with code referencing tauri-apps/api #7488

Closed
binhlt0402 opened this issue Jul 24, 2023 · 6 comments
Labels
status: needs triage This issue needs to triage, applied to new issues type: bug

Comments

@binhlt0402
Copy link

Describe the bug

Similar to #6554 I tried to build a project with a line code using appWindow.setSize to resize the windows and I got the same error when run npm run tauri build. I tried several work around mention in #6554 and none of them working. Since my issue is with Nextjs instead of SvelteKit I created a new bug report instead of comment in #6554.

Reproduction

  1. Create new tauri project using nextjs front end following the guide for nextjs front-end
  2. Import and use @tauri-apps/api in page.tsx and add
import("@tauri-apps/api").then((tauri) => {
    tauri.window.appWindow.setSize(new tauri.window.LogicalSize(360, 500));
})

right below Home() {
3. Add 'use client' to the beginning of page.tsx
4. Add "allowlist": { "window": { "all": true } }, to tauri.conf.json
5. Build using 'npm run tauri build'

Expected behavior

It should build a working Tauri bundle

Platform and versions

[✔] Environment
    - OS: Windows 10.0.19045 X64
    ✔ WebView2: 115.0.1901.183
    ✔ MSVC: Visual Studio Community 2019
    ✔ rustc: 1.70.0 (90c541806 2023-05-31)
    ✔ Cargo: 1.70.0 (ec8a8a0ca 2023-04-25)
    ✔ rustup: 1.26.0 (5af9b9484 2023-04-05)
    ✔ Rust toolchain: stable-x86_64-pc-windows-msvc (default)
    - node: 16.15.0
    - yarn: 1.22.18
    - npm: 9.7.2

[-] Packages
    - tauri [RUST]: 1.4.1
    - tauri-build [RUST]: 1.4.0
    - wry [RUST]: 0.24.3
    - tao [RUST]: 0.16.2
    - @tauri-apps/api [NPM]: 1.4.0
    - @tauri-apps/cli [NPM]: 1.4.0

[-] App
    - build-type: bundle
    - CSP: unset
    - distDir: ../out
    - devPath: http://localhost:3000/
    - framework: React (Next.js)
    - bundler: Webpack

Stack trace

[=   ] - info Generating static pages (0/4)(node:36656) ExperimentalWarning: buffer.Blob is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
my path\.next\server\chunks\411.js:49
var S={};e(S,{CloseRequestedEvent:()=>y,LogicalPosition:()=>chunk_NMUKSDLG_c,LogicalSize:()=>chunk_NMUKSDLG_m,PhysicalPosition:()=>r,PhysicalSize:()=>o,UserAttentionType:()=>chunk_NMUKSDLG_W,WebviewWindow:()=>chunk_NMUKSDLG_s,WebviewWindowHandle:()=>chunk_NMUKSDLG_u,WindowManager:()=>b,appWindow:()=>g,availableMonitors:()=>chunk_NMUKSDLG_D,currentMonitor:()=>C,getAll:()=>h,getCurrent:()=>chunk_NMUKSDLG_E,primaryMonitor:()=>T});var chunk_NMUKSDLG_m=class{constructor(e,a){this.type="Logical";this.width=e,this.height=a}},o=class{constructor(e,a){this.type="Physical";this.width=e,this.height=a}toLogical(e){return new chunk_NMUKSDLG_m(this.width/e,this.height/e)}},chunk_NMUKSDLG_c=class{constructor(e,a){this.type="Logical";this.x=e,this.y=a}},r=class{constructor(e,a){this.type="Physical";this.x=e,this.y=a}toLogical(e){return new chunk_NMUKSDLG_c(this.x/e,this.y/e)}},chunk_NMUKSDLG_W=(a=>(a[a.Critical=1]="Critical",a[a.Informational=2]="Informational",a))(chunk_NMUKSDLG_W||{});function chunk_NMUKSDLG_E(){return new chunk_NMUKSDLG_s(window.__TAURI_METADATA__.__currentWindow.label,{skip:!0})}function h(){return window.__TAURI_METADATA__.__windows.map(t=>new chunk_NMUKSDLG_s(t.label,{skip:!0}))}var M=["tauri://created","tauri://error"],chunk_NMUKSDLG_u=class{constructor(e){this.label=e,this.listeners=Object.create(null)}async listen(e,a){return this._handleTauriEvent(e,a)?Promise.resolve(()=>{let n=this.listeners[e];n.splice(n.indexOf(a),1)}):chunk_M3Y6ZK7U_a(e,this.label,a)}async once(e,a){return this._handleTauriEvent(e,a)?Promise.resolve(()=>{let n=this.listeners[e];n.splice(n.indexOf(a),1)}):chunk_M3Y6ZK7U_u(e,this.label,a)}async emit(e,a){if(M.includes(e)){for(let n of this.listeners[e]||[])n({event:e,id:-1,windowLabel:this.label,payload:a});return Promise.resolve()}return m(e,this.label,a)}_handleTauriEvent(e,a){return M.includes(e)?(e in this.listeners?this.listeners[e].push(a):this.listeners[e]=[a],!0):!1}},b=class extends chunk_NMUKSDLG_u{async scaleFactor(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"scaleFactor"}}}})}async innerPosition(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"innerPosition"}}}}).then(({x:e,y:a})=>new r(e,a))}async outerPosition(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"outerPosition"}}}}).then(({x:e,y:a})=>new r(e,a))}async innerSize(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"innerSize"}}}}).then(({width:e,height:a})=>new o(e,a))}async outerSize(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"outerSize"}}}}).then(({width:e,height:a})=>new o(e,a))}async isFullscreen(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isFullscreen"}}}})}async isMinimized(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isMinimized"}}}})}async isMaximized(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isMaximized"}}}})}async isFocused(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isFocused"}}}})}async isDecorated(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isDecorated"}}}})}async isResizable(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isResizable"}}}})}async isMaximizable(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isMaximizable"}}}})}async isMinimizable(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isMinimizable"}}}})}async isClosable(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isClosable"}}}})}async isVisible(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"isVisible"}}}})}async title(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"title"}}}})}async theme(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"theme"}}}})}async center(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"center"}}}})}async requestUserAttention(e){let a=null;return e&&(e===1?a={type:"Critical"}:a={type:"Informational"}),chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"requestUserAttention",payload:a}}}})}async setResizable(e){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setResizable",payload:e}}}})}async setMaximizable(e){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setMaximizable",payload:e}}}})}async setMinimizable(e){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setMinimizable",payload:e}}}})}async setClosable(e){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setClosable",payload:e}}}})}async setTitle(e){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setTitle",payload:e}}}})}async maximize(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"maximize"}}}})}async unmaximize(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"unmaximize"}}}})}async toggleMaximize(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"toggleMaximize"}}}})}async minimize(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"minimize"}}}})}async unminimize(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"unminimize"}}}})}async show(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"show"}}}})}async hide(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"hide"}}}})}async close(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"close"}}}})}async setDecorations(e){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setDecorations",payload:e}}}})}async setAlwaysOnTop(e){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setAlwaysOnTop",payload:e}}}})}async setContentProtected(e){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setContentProtected",payload:e}}}})}async setSize(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setSize",payload:{type:e.type,data:{width:e.width,height:e.height}}}}}})}async setMinSize(e){if(e&&e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setMinSize",payload:e?{type:e.type,data:{width:e.width,height:e.height}}:null}}}})}async setMaxSize(e){if(e&&e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `size` argument must be either a LogicalSize or a PhysicalSize instance");return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setMaxSize",payload:e?{type:e.type,data:{width:e.width,height:e.height}}:null}}}})}async setPosition(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setPosition",payload:{type:e.type,data:{x:e.x,y:e.y}}}}}})}async setFullscreen(e){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setFullscreen",payload:e}}}})}async setFocus(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setFocus"}}}})}async setIcon(e){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setIcon",payload:{icon:typeof e=="string"?e:Array.from(e)}}}}})}async setSkipTaskbar(e){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setSkipTaskbar",payload:e}}}})}async setCursorGrab(e){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorGrab",payload:e}}}})}async setCursorVisible(e){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorVisible",payload:e}}}})}async setCursorIcon(e){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorIcon",payload:e}}}})}async setCursorPosition(e){if(!e||e.type!=="Logical"&&e.type!=="Physical")throw new Error("the `position` argument must be either a LogicalPosition or a PhysicalPosition instance");return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setCursorPosition",payload:{type:e.type,data:{x:e.x,y:e.y}}}}}})}async setIgnoreCursorEvents(e){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"setIgnoreCursorEvents",payload:e}}}})}async startDragging(){return chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"manage",data:{label:this.label,cmd:{type:"startDragging"}}}})}async onResized(e){return this.listen("tauri://resize",a=>{a.payload=chunk_NMUKSDLG_f(a.payload),e(a)})}async onMoved(e){return this.listen("tauri://move",a=>{a.payload=v(a.payload),e(a)})}async onCloseRequested(e){return this.listen("tauri://close-requested",a=>{let n=new y(a);Promise.resolve(e(n)).then(()=>{if(!n.isPreventDefault())return this.close()})})}async onFocusChanged(e){let a=await this.listen("tauri://focus",d=>{e({...d,payload:!0})}),n=await this.listen("tauri://blur",d=>{e({...d,payload:!1})});return()=>{a(),n()}}async onScaleChanged(e){return this.listen("tauri://scale-change",e)}async onMenuClicked(e){return this.listen("tauri://menu",e)}async onFileDropEvent(e){let a=await this.listen("tauri://file-drop",l=>{e({...l,payload:{type:"drop",paths:l.payload}})}),n=await this.listen("tauri://file-drop-hover",l=>{e({...l,payload:{type:"hover",paths:l.payload}})}),d=await this.listen("tauri://file-drop-cancelled",l=>{e({...l,payload:{type:"cancel"}})});return()=>{a(),n(),d()}}async onThemeChanged(e){return this.listen("tauri://theme-changed",e)}},y=class{constructor(e){this._preventDefault=!1;this.event=e.event,this.windowLabel=e.windowLabel,this.id=e.id}preventDefault(){this._preventDefault=!0}isPreventDefault(){return this._preventDefault}},chunk_NMUKSDLG_s=class extends b{constructor(e,a={}){super(e),a?.skip||chunk_RKMHWDGH_a({__tauriModule:"Window",message:{cmd:"createWebview",data:{options:{label:e,...a}}}}).then(async()=>this.emit("tauri://created")).catch(async n=>this.emit("tauri://error",n))}static getByLabel(e){return h().some(a=>a.label===e)?new chunk_NMUKSDLG_s(e,{skip:!0}):null}static async getFocusedWindow(){for(let e of h())if(await e.isFocused())return e;return null}},g;"__TAURI_METADATA__"in window?g=new chunk_NMUKSDLG_s(window.__TAURI_METADATA__.__currentWindow.label,{skip:!0}):(console.warn(`Could not find "window.__TAURI_METADATA__". The "appWindow" value will reference the "main" window label.

Additional context

No response

@binhlt0402 binhlt0402 added status: needs triage This issue needs to triage, applied to new issues type: bug labels Jul 24, 2023
@binhlt0402
Copy link
Author

I found out just adding if (typeof window === 'undefined') return before will make it work. This won't work if we use the normal import. So the correct use of tauri-apps/api/window in nextjs is

if (typeof window === 'undefined') return
import("@tauri-apps/api").then((tauri) => {
    // use the api
    tauri.window....
})

@erayerdin
Copy link

Sorry for reviving the zombies but I have various custom hooks throughout my codebase and I heavily use invoke. I always get build error on npm run build. The stacktrace is not helpful at all, even with next build --no-mangling.

I just want to verify this: Do I have to do import("@tauri-apps/api").then everytime I use invoke?

For reference, one of my custom hook looks as below:

export const useOverviewInfo = (): OverviewInfo => {
  const [ currentCpuUsage, setCurrentCpuUsage ] = useState<number>(0);
  const [ currentMemoryUsage, setCurrentMemoryUsage ] = useState<number>(0);
  const [ totalMemoryAmount, setTotalMemoryAmount ] = useState<number>(0);
  const [ currentDiskUsage, setCurrentDiskUsage ] = useState(0);
  const [ totalDiskAmount, setTotalDiskAmount ] = useState(0);

  console.trace("states", { currentCpuUsage, currentMemoryUsage, totalMemoryAmount, currentDiskUsage, totalDiskAmount });

  const fetchCpuUsage = async () => {
    console.log("Fetching cpu usage...");
    const cpuUsage: number = await invoke('get_cpu_usage');
    console.trace("Cpu usage", cpuUsage);
    return cpuUsage;
  };

  const fetchMemoryUsage = async () => {
    console.log("Fetching memory usage...");
    const memoryUsage: number = (await invoke('get_memory_usage')) as number / (1024 * 1024 * 1024);
    console.trace("Memory usage", memoryUsage);
    return memoryUsage;
  };

  const fetchTotalMemoryAmount = async () => {
    console.log("Fetching total memory amount...");
    const totalMemory: number = (await invoke('get_total_memory')) as number / (1024 * 1024 * 1024);
    console.trace("Total memory", totalMemory);
    return totalMemory;
  };

  const fetchCurrentDiskUsage = async () => {
    console.log("Fetching current disk usage...");
    const diskUsage: number = (await invoke('get_disk_usage')) as number / (1024 * 1024 * 1024);
    console.trace("Disk usage", diskUsage);
    return diskUsage;
  };

  const fetchTotalDiskAmount = async () => {
    console.log("Fetching total disk amount...");
    const totalDisk: number = (await invoke('get_total_disk')) as number / (1024 * 1024 * 1024);
    console.trace("Total disk", totalDisk);
    return totalDisk;
  };

  useEffect(() => {
    fetchTotalMemoryAmount().then((val) => setTotalMemoryAmount(val));
    fetchCurrentDiskUsage().then((val) => setCurrentDiskUsage(val));
    fetchTotalDiskAmount().then((val) => setTotalDiskAmount(val));
  }, []);

  useEffect(() => {
    const interval = setInterval(() => {
      fetchCpuUsage().then((val) => setCurrentCpuUsage(val));
      fetchMemoryUsage().then((val) => setCurrentMemoryUsage(val));
    }, 1000);
    return () => {
      console.info("Clearing fetchCpuUsage and fetchMemoryUsage intervals...");
      clearInterval(interval);
    };
  }, [currentCpuUsage, currentMemoryUsage]);

@FabianLars
Copy link
Member

I just want to verify this: Do I have to do import("@tauri-apps/api").then everytime I use invoke?

if we're specifically talking about invoke then you can change the import to import { invoke } from "@tauri-apps/api/tauri" (note the tauri module at the end) and you should not hit any issues with that.

@erayerdin
Copy link

Thank you so much, I've trying to solve this for 2 days now. Search results lacked so much info. Might be a dumb question, but may I ask: Why importing from @tauri-apps/api/tauri does not cause this error?

@FabianLars
Copy link
Member

The problematic modules are window and fs (or path? can't remember) because they declare variables and not just functions which use the global window or navigator objects to resolve their value.
If you import something from the package root, all modules will be in scope / evaluated for some reason and if you import from a sub-module directly that isn't the case and therefore works fine in SSR contexts.

@davegomez
Copy link

I wanted to create an utility function to spawn new windows from anywhere in the application so just the Promise example provided in this issue wouldn't work.

I wrote it in a slightly different way so you can reuse the function and I hope it can help somebody dealing with this issue.

export interface WebviewOptions {
  label: string;
  onCreated?: (event?: Event<unknown>) => void;
  onError?: (event?: Event<unknown>) => void;
  onDestroyed?: (event?: Event<unknown>) => void;
}

function noop() {
  /* Do nothing */
}

export function spawnWebview(
  {
    label,
    onCreated = noop,
    onError = noop,
    onDestroyed = noop,
  }: WebviewOptions,
  options = {},
) {
  // Before proceeding, it checks whether the window object is defined. If it's
  // not (which may happen in certain non-browser environments like Node.js),
  // the function exits immediately.
  if (typeof window === 'undefined') {
    return;
  }

  import('@tauri-apps/api/window').then(({ WebviewWindow }) => {
    const webview = new WebviewWindow(label, {
      url: 'index.html',
      ...options,
    });

    webview
      .once('tauri://created', () => onCreated())
      .catch((e) => throwNewError('created', e));

    webview
      .once('tauri://error', (e) => onError(e))
      .catch((e) => throwNewError('error', e));

    webview
      .once('tauri://destroyed', () => onDestroyed())
      .catch((e) => throwNewError('destroyed', e));
  });
}

function throwNewError(type: string, e?: Error) {
  throw new Error(`Webview ${type} event failed with error: `, e);
}

This also should work with other APIs different than WebviewWindow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: needs triage This issue needs to triage, applied to new issues type: bug
Projects
None yet
Development

No branches or pull requests

4 participants